android-developerauto-recovered
android-developerauto-recovered
Bachelor of Technology
In
SANAPATHI NAGASATISH
(22135A0429)
2024-2025
1
CERTIFICATE
CERTIFICATE:
ACKNOWLEDGEMENT
SANAPATHI NAGASATISH
(22135A0429)
ABSTRACT
Android app development using Kotlin has witnessed remarkable growth and adoption since
Google endorsed it as an official language in 2017. Statistics reveal that over 80% of the top
1000 Android apps on the Google Play Store are leveraging Kotlin for development, indicating a
strong preference among developers. This surge in adoption is attributed to Kotlin's modern
language features, improved syntax, and enhanced productivity compared to Java. Developers
praise Kotlin for its concise syntax, null safety, extension functions, and support for coroutines,
which facilitate asynchronous programming. Moreover, Kotlin's seamless interoperability with
Java enables developers to gradually transition existing codebases to Kotlin, ensuring a smooth
migration process.
Performance-wise, Kotlin offers tangible benefits over Java, with reduced boilerplate code
and increased developer efficiency. Feedback from developers consistently highlights the
language's expressiveness, safety features, and ease of integration with existing Android tools
and libraries. The satisfaction level among developers using Kotlin for Android development
remains high, supported by its robust community and ongoing support from Google and
JetBrains, the language's creators.
Through a blend of guided tutorials, coding challenges, and real-world projects, interns engage in
the complete app development lifecycle, from conceptualization to deployment. The abstract
highlights the program's emphasis on fostering collaboration, innovation, and problem-solving
abilities within a virtual environment. Drawing on participant testimonials and performance
evaluations, it evaluates the effectiveness of the internship in enhancing technical proficiency
and preparing individuals for careers in mobile app development. Furthermore, it discusses the
scalability and potential impact of virtual internships in democratizing access to practical
learning opportunities. By shedding light on the Google Android Developer Virtual Internship,
this abstract contributes to the discourse on innovative approaches to skill development in the
digital age.
INTRODUCTION
In this introduction, we delve into the structure, objectives, and significance of the
Google Android Development Virtual Internship, shedding light on its role in shaping the next
generation of Android developers. We explore how the program leverages Google's expertise in
Android development to provide participants with a comprehensive learning experience,
fostering collaboration, innovation, and professional growth within a virtual environment.
The introduction sets the stage for understanding the transformative impact of the Google
Android Development Virtual Internship, highlighting its contributions to skill development,
industry readiness, and the broader ecosystem of technology education. Through an exploration
of its key components and underlying principles, we aim to provide a comprehensive overview
of this ground-breaking initiative and its implications for the future of Android app
development.
INDEX
ANDROID DEVELOPMENT
MODULE 1. YOUR FIRST ANDROID APP (09 - 22)
1.1. Introduction to Kotlin
1.2. Set Up Android Studio
1.3. Build a Basic Layout
We need something called the Kotlin compiler, which takes the Kotlin code you wrote, looks at
it line by line, and translates it into something that the computer can understand. This process is
called compiling the code.
If the code compiles successfully, your program will run (or execute). When the computer
executes your program, it performs each of your instructions.
In your code, you define a function first. That means you specify all the instructions needed to
perform that task.
Once the function is defined, then you can call that function, so the instructions within that
function can be performed or executed.
Define a function
• The function can also require some inputs, or information that needs to be provided
when the function is called. The function uses these inputs to accomplish its purpose.
Requiring inputs is optional, and some functions do not require inputs.
• The function also has a body which contains the instructions to perform the task.
Function Name
Functions have names so they can be distinguished from each other, similar to how people have
names to identify themselves. The name of the function is found after the fun keyword.
The purpose of following the style guide is to make your code easier to read and more
consistent with how other Android developers write their code. This consistency is important
when collaborating on large projects together, so that the style of code is the same throughout all
the files in the project.
Here are some of the relevant style guide recommendations for what you've learned in Kotlin so
far:
• Function names should be in camel case and should be verbs or verb phrases.
• The opening curly brace should appear at the end of the line where the function begins.
11
1. Open any web browser and navigate to the Android Studio download page.
This is the Android Developers website, where you can download Android Studio. This page
automatically detects your operating system.
2. Click Download Android Studio. The Terms and Conditions page with the Android
Studio License Agreement opens.
4. At the bottom of the page, if you agree with the terms and conditions, select the I have
read and agree with the above terms and conditions checkbox.
6. When prompted, save the file to a location where you can easily locate it, such as the
Downloads folder.
7. Wait for the download to complete. This may take a while and may be a good moment to
enjoy some tea!
3. If you see a User Account Control dialog about allowing the installation to make
changes to your computer, click Yes to confirm the installation.
7. Choose your preference of light or dark theme when Android Studio first launches.
Screenshots in this course use the light theme, but choose whichever one you
prefer.
8. During the installation, the setup wizard downloads and installs additional components
and tools needed for Android app development. This may take some time depending on
your internet speed. During this time, you may see a User Account Control dialog for
Windows Command Processor. Click Yes to accept the dialog.
9. You may also receive a Windows Security Alert about adb.exe. Click Allow Access, if
needed, to continue the installation.
The Welcome to Android Studio window displays and you're ready to start creating apps.
The New Project window opens with a list of templates provided by Android Studio.
In Android Studio, a project template is an Android project that provides the blueprint for a
certain type of app. Templates create the structure of the project and the files needed for
Android Studio to build your project. The template that you choose provides a starter code to
get you going faster.
3. Make sure the Phone and Tablet tab is selected.
4. Click the Empty Activity template to select it as the template for your project. The
13
Empty Activity template is the template to create a simple project that you can use to
build a Compose app. It has a single screen and displays the text "Hello Android!".
14
5. Click Next. The New Project dialog opens. This has some fields to configure your project.
7. The Name field is used to enter the name of your project, for this codelab type "Greeting
Card".
8. Leave the Package name field as is. This is how your files will be organized in the file
structure. In this case, the package name will be com.example.greetingcard.
9. Leave the Save location field as is. It contains the location where all the files related to
your project are saved. Take a note of where that is on your computer so that you can
find your files.
10. Select API 24: Android 7.0 (Nougat) from the menu in the Minimum SDK field.
Minimum SDK indicates the minimum version of Android that your app can run on.
11. Click Finish. This may take a while - this is a great time to get a cup of tea! While
Android Studio is setting up, a progress bar and message indicates whether Android
Studio is still setting up your project. It may look like this:
12. You may see a What's New pane which contains updates on new features in Android
Studio. Close it for now.
13. Click Split on the top right of Android Studio, this allows you to view both code and
design. You can also click Code to view code only or click Design to view design only.
The Project view (1) shows the files and folders of your project
The Design view (3) is where you preview what your app looks like
15. In the Design view, you will see a blank pane with this text:
16. Click Build & Refresh. It may take a while to build but when it is done the preview
shows a text box that says "Hello Android!". Empty Compose activity contains all the
code necessary to create this app.
15
1. In Android Studio, take a look at the Project tab. The Project tab shows the files and
folders of your project. When you were setting up your project the package name was
com.example.greetingcard. You can see that package right here in the Project tab. A
package is basically a folder where code is located. Android Studio organizes the project
in a directory structure made up of set of packages.
2. If necessary, select Android from the drop-down menu in the Project tab.
3. Select Project Source Files from the drop-down menu. You can now browse the files in
the same way as in any file browser.
4. Select Android again to switch back to the previous view. You use the Android view for
this course. If your file structure ever looks strange, check to make sure you're still in
Android view.
5. If there's a download link next to S, click Download > Accept > Next > Finish. The
presence of the download link indicates that the image isn't installed on your computer,
in which case you must install the image before you can configure the virtual device.
Expect the download to take some time to complete.
16
6. In the Recommended tab, choose S as the version of Android to run on the virtual device.
17
7. In the AVD Name field, enter a name for your AVD or use the default. Leave the rest of
the fields unchanged.
8. Click Finish.
2. Click .
The virtual device starts just like a physical device. Expect this to take a while—potentially
several minutes—for the emulator to start for the first time. The virtual device should open
beside the code editor.
18
3. If prompted, enter your device password or pin. You know you succeeded when
you see a You are now a developer! message.
19
6. Tap Developer options and then tap the USB debugging toggle
to turn it on.
1. Connect your Android device to your computer with a USB cable. A dialog should
appear on your device, which asks you to allow USB debugging.
2. Select the Always allow from this computer checkbox and then tap OK.
3. In Android Studio on your computer, make sure your device is selected in the dropdown.
Click .
4. Select your device and then click OK. Android Studio installs the app on your device and
runs it.
5. If your device runs an Android platform that isn't installed in Android Studio and you see
a message that asks whether you want to install the needed platform, click Install >
Continue > Finish. Android Studio installs the app on your device and runs it.
Note: For Android Studio 3.6 and higher, the physical device is automatically selected when
the device is connected with debugging turned on.
20
2. In the New Project dialog, select Empty Activity and then click Next.
3. In the Name field enter Happy Birthday and then select a minimum API level of 24
(Nougat) in the Minimum SDK field and click Finish.
4. Wait for Android Studio to create the project files and build the project.
21
Composable functions
Composable functions are the basic building block of a UI in Compose. A composable function:
Annotations
Annotations are means of attaching extra information to code. This information
helps tools like the Jetpack Compose compiler, and other developers understand the
app's code.
An annotation is applied by prefixing its name (the annotation) with the @ character
at the beginning of the declaration you are annotating. Different code elements,
including properties, functions, and classes, can be annotated. Later on in the
course, you'll learn about classes.
22
3. In the file browser, select the image file that you downloaded and then click Open.
Android Studio shows you a preview of the image. Select Density from the QUALIFIER
TYPE drop-down list. You'll learn why you're doing this, in a later section.
5. Android Studio shows you a preview of the image. Select Density from the QUALIFIER
TYPE drop-down list. You'll learn why you're doing this, in a later section.
23
7. Click Next. 8. Android Studio shows you the folder structure in which your image will
be placed.
Notice the drawable-nodpi folder.
9. Click Import(C).
10. Switch back to the project view, click View > Tool Windows > Project or click the
Project tab on the far left.
Click app > res > drawable to confirm that the image is in the drawable folder.
24
MODULE 2
BUILDING APP
UI
Imagine that you build a program that tells drivers what they should do when they're at a
traffic light. Focus on the first condition: a red traffic light. What do you do at a red traffic
light? Stop!
In Kotlin, you can express this condition with an if statement. Take a look at the anatomy
of an if statement:
To use if statements, you need to use the if keyword followed by the condition that you
want to evaluate. You need to express the condition with a boolean expression.
Expressions combine values, variables, and operators that return a value. Boolean
expressions return a boolean value.
The if statement can also contain the if branch and else if branches without any else branch:
25
You can also use conditionals as expressions to return different values for each
branch of condition. When the body of each branch appears similar, you can use
conditional expressions to improve code readability compared to conditional
statements
26
The syntax for conditionals as expressions is similar to statements, but the last line of bodies in
each branch need to return a value or an expression, and the conditionals are assigned to a
variable. If the bodies only contain a return value or expression, you can remove the curly
braces to make the code more concise.
27
• Constructors. A special member function that creates instances of the class throughout
the program in which it's defined.
The Kotlin runtime uses the class, or blueprint, to create an object of that particular type.
With the SmartDevice class, you have a blueprint of what a smart device is. To have an actual
smart device in your program, you need to create a SmartDevice object instance. The
instantiation syntax starts with the class name followed by a set of parentheses as you can see in
this diagram:
To use an object, you create the object and assign it to a variable, similar to how you define a
variable. You use the val keyword to create an immutable variable and the var keyword for a
mutable variable. The val or var keyword is followed by the name of the variable, then an =
assignment operator, then the instantiation of the class object.
• Device status. Whether the device is on, off, online, or offline. The device is considered
online when it's connected to the internet. Otherwise, it's considered offline.
Properties are basically variables that are defined in the class body instead of the function body.
This means that the syntax to define properties and variables are identical. You define an
28
immutable property with the val keyword and a mutable property with the var keyword.
29
Defining a Constructor:
Constructors initialize an object and make the object ready for use. You did this when you
instantiated the object. The code inside the constructor executes when the object of the class is
instantiated. You can define a constructor with or without parameters.
Default constructor
A default constructor is a constructor without parameters.
The constructor now accepts parameters to set up its properties, so the way to instantiate an
object for such a class also changes.
You can see the full syntax to instantiate an object in this diagram:
• Primary constructor. A class can have only one primary constructor, which is defined as
part of the class header. A primary constructor can be a default or parameterized
constructor. The primary constructor doesn't have a body. That means that it can't
contain any code.
• Secondary constructor. A class can have multiple secondary constructors. You can
define the secondary constructor with or without parameters. The secondary constructor
can initialize the class and has a body, which can contain initialization logic. If the class
has a primary constructor, each secondary constructor needs to initialize the primary
constructor.
You can use the primary constructor to initialize properties in the class header. The arguments
passed to the constructor are assigned to the properties. The syntax to define a primary
constructor
30
starts with the class name followed by the constructor keyword and a set of parentheses. The
parentheses contain the parameters for the primary constructor. If there's more than one
parameter, commas separate the parameter definitions. You can see the full syntax to define a
primary constructor in this diagram:
The secondary constructor is enclosed in the body of the class and its syntax includes three parts:
• Secondary constructor declaration. The secondary constructor definition starts with the
constructor keyword followed by parentheses. If applicable, the parentheses contain the
parameters required by the secondary constructor.
• Primary constructor initialization. The initialization starts with a colon followed by the
this keyword and a set of parentheses. If applicable, the parentheses contain the
parameters required by the primary constructor.
2. In the New Project dialog, select Empty Activity and then click Next.
4. In the Minimum SDK field, select a minimum API level of 24 (Nougat) from the menu
and then click Finish.
This composable function represents the UI components of the layout and also holds the
buttonclick and image-display logic.
Because this app only consists of a button and an image, think of this composable function as
the app itself. That's why it's called the DiceRollerApp() function.
5. Delete all of the code inside the setContent{} lambda found in the onCreate() method.
32
6. In the setContent{} lambda body, call the DiceRollerTheme{} lambda and then inside
the DiceRollerTheme{} lambda, call the DiceRollerApp() function.
Add a modifier
Compose uses a Modifier object, which is a collection of elements that decorate or modify the
behavior of Compose UI elements. You use this to style the UI components of the Dice Roller
app's components.
The Column() function is a composable layout that places its children in a vertical sequence. In
the expected app design, you can see that the dice image displays vertically above the roll
button:
33
The modifier argument ensures that the composables in the Column() function adhere to the
constraints called on the modifier instance.
3. Pass a horizontalAlignment argument to the Column() function and then set it to a value
of Alignment.CenterHorizontally.
This ensures that the children within the column are centered on the device screen with respect
to the width.
Add a Button:
res/values/strings.xml
<string name="roll">Roll</string>
3. In the MainActivity.kt file, add a Text() function to the Button() in the lambda body of
the function.
4. Pass the string resource ID of the roll string to the stringResource() function and pass the
result to the Text composable.
Now that all the necessary composables are present, you modify the app so that a tap of the
button rolls the dice.
2. Take a look at the Button composable. You will notice that it is being passed an onClick
parameter which is set to a pair of curly braces with the comment /*TODO*/ inside the
braces. The braces, in this case, represent what is known as a lambda, the area inside of
the braces being the lambda body. When a function is passed as an argument, it can also
be referred to as a " callback".
34
3. In the Button() function, remove the /*TODO*/ comment from the value of the lambda
body of the onClick parameter.
4. A dice roll is random. To reflect that in code, you need to use the correct syntax to
generate a random number. In Kotlin, you can use the random() method on a number
range. In the onClick lambda body, set the result variable to a range between 1 to 6 and
then call the random() method on that range. Remember that, in Kotlin, ranges are
designated by two periods between the first number in the range and the last number in
the range.
1. At the beginning of the EditNumberField() function, use the val keyword to add an
amountInput variable set it to "0" value:
2. Set the value named parameter to an amountInput valueheck the preview. The text box
displays the value set to the state variable as you can see in this image:
3. Run the app in the emulator, try to enter a different value. The hardcoded state
remains unchanged because the TextField composable doesn't update itself. It updates
when its value parameter changes, which is set to the amountInput property.
The amountInput variable represents the state of the text box. Having a hardcoded state isn't
useful because it can't be modified and it doesn't reflect user input. You need to update the state
of the app when the user updates the bill amount.
35
Testing is an important part of the app development process. By running tests against your app
consistently, you can verify your app's correctness, functional behavior, and usability before you
release it publicly.
Testing also provides a way to continuously check the existing code as changes are
To grow your codebase, you need to test existing functionality as you add new pieces,
which is only possible if you have existing tests. As your app grows, manual testing
takes much more effort than automated testing. Furthermore, once you start
working on apps in production, testing becomes critical when you have a large user
base. For example, you must account for many different types of devices running
many different versions of Android.
Eventually, you reach a point where automated tests can account for the majority of usage
scenarios significantly faster than manual tests. When you run tests before you release new
code, you can make changes to the existing code so that you avoid the release of an app with
unexpected behaviors.
Remember that automated tests are tests executed through software, as opposed to manual tests,
which are carried out by a person who directly interacts with a device. Automated testing and
manual testing play a critical role in ensuring that users of your product have a pleasant
experience. However, automated tests can be more precise and they optimize your team's
productivity because a person isn't required to run them and they can be executed much faster
than a manual test. Type of automated tests
Local tests
Local tests are a type of automated test that directly test a small piece of code to ensure that it
functions properly. With local tests, you can test functions, classes, and properties. Local tests
are executed on your workstation, which means they run in a development environment without
the need for a device or emulator. This is a fancy way to say that local tests run on your
computer. They also have very low overhead for computer resources, so they can run fast even
with limited resources. Android Studio comes ready to run local tests automatically.
36
Instrumentation tests
For Android development, an instrumentation test is a UI test. Instrumentation tests let you test
parts of an app that depend on the Android API, and its platform APIs and services.
Unlike local tests, UI tests launch an app or part of an app, simulate user interactions, and check
whether the app reacted appropriately. Throughout this course, UI tests are run on a physical
device or emulator.
When you run an instrumentation test on Android, the test code is actually built into its own
Android Application Package (APK) like a regular Android app. An APK is a compressed file
that contains all the code and necessary files to run the app on a device or emulator. The test
APK is installed on the device or emulator along with the regular app APK.
37
MODULE 3
DISPLAY LISTS AND USE MATERIAL DESIGN
A generic data type is provided when instantiating a class, so it needs to be defined as part of the
class signature. After the class name comes a left-facing angle bracket (<), followed by a
placeholder name for the data type, followed by a right-facing angle bracket (>).
The placeholder name can then be used wherever you use a real data type within the class, such
as for a property.
This is identical to any other property declaration, except the placeholder name is used instead
of the data type. Enum_Class:
An enum class is used to create types with a limited set of possible values. In the real world, for
example, the four cardinal directions—north, south, east, and west—could be represented by an
enum class. There's no need, and the code shouldn't allow, for the use of any additional
directions.
The syntax for an enum class is shown below.
38
39
Each possible value of an enum is called an enum constant. Enum constants are placed inside
the curly braces separated by commas. The convention is to capitalize every letter in the
constant name.
Like a grouping of solar panels is called a solar array, or how learning Kotlin opens up an array
of possibilities for your programming career, an Array represents more than one value.
Specifically, an array is a sequence of values that all have the same data type.
• The elements in an array are ordered and are accessed with an index.
What's an index? An index is a whole number that corresponds to an element in the array. An
index tells the distance of an item from the starting element in an array. This is called zero-
indexing. The first element of the array is at index 0, the second element is at index 1, because
it's one place from the first element, and so on.
40
• Accessing an array element by its index is fast. You can access any random element of
an array by its index and expect it to take about the same amount of time to access any
other random element. This is why it's said that arrays have random access.
An array has a fixed size. This means that you can't add elements to an array beyond this size.
Trying to access the element at index 100 in a 100 element array will throw an exception
because the highest index is 99 (remember that the first index is 0, not 1). You can, however,
modify the values at indexes in the array.
Lists:
A list is an ordered, resizable collection, typically implemented as a resizable array. When the
array is filled to capacity and you try to insert a new element, the array is copied to a new bigger
array.
With a list, you can also insert new elements between other elements at a specific index.
41
This is how lists are able to add and remove elements. In most cases, it takes the same amount of
time to add any element to a list, regardless of how many elements are in the list. Every once in
a while, if adding a new element would put the array above its defined size, the array elements
might have to move to make room for new elements. Lists do all of this for you, but behind the
scenes, it's just an array that gets swapped out for a new array when needed.
The collection types you'll encounter in Kotlin implement one or more interfaces. As you
learned in the Generics, objects, and extensions codelab earlier in this unit, interfaces provide a
standard set of properties and methods for a class to implement. A class that implements the List
interface provides implementations for all the properties and methods of the List interface. The
same is true for MutableList.
• List is an interface that defines properties and methods related to a read-only ordered
collection of items.
• MutableList extends the List interface by defining methods to modify a list, such as
adding and removing elements.
These interfaces only specify the properties and methods of a List and/or MutableList. It's up to
the class that extends them to determine how each property and method is implemented. The
arraybased implementation described above is what you'll use most, if not all of the time, but
Kotlin allows other classes to extend List and MutableList.
42
Sets:
A set is a collection that does not have a specific order and does not allow duplicate values.
Map Collection:
A Map is a collection consisting of keys and values. It's called a map because unique keys are
mapped to other values. A key and its accompanying value are often called a key-value pair.
A map's keys are unique. A map's values, however, are not. Two different keys could map to the
same value. For example, "Mercury" has 0 moons, and "Venus" has 0 moons.
Accessing a value from a map by its key is generally faster than searching through a large list,
such as with indexOf().
Maps can be declared using the mapOf() or mutableMapOf() function. Maps require two generic
types separated by a comma—one for the keys and another for the values.
43
A map can also use type inference if it has initial values. To populate a map with initial values,
each key value pair consists of the key, followed by the to operator, followed by the value. Each
pair is separated by a comma.
These collection types allow you to group and organize values in your code. Arrays and lists
provide fast access to elements by their index, while sets and maps use hash codes to make it
easier to find elements in the collection.
In Android apps, lists are made up of list items. For single pieces of data, this could be
something simple like a string or an integer. For list items that have multiple pieces of data, like
an image and text, you will need a class that contains all of these properties. Data classes are a
type of class that only contain properties, they can provide some utility methods to work with
those properties.
44
Name the new package model. The model package will contain the data model that will be
represented by a data class. The data class will be comprised of properties that represent the
information relevant to what will be an "Affirmation," which will consist of a string resource
and an image resource. Packages are directories that contain classes and even other directories.
45
3. Each Affirmation consists of one image and one string. Create two val properties in the
Affirmation data class. One should be called stringResourceId and the other
imageResourceId. They should both be integers.
4. Annotate the stringResourceId property with the @StringRes annotation and annotate
the imageResourceId with the @DrawableRes annotation.
46
The stringResourceId represents an ID for the affirmation text stored in a string resource.
The imageResourceId represents an ID for the affirmation image stored in a drawable
resource.
uncomment the two import statements and the contents of the class.
2. Open com.example.woof > data > Dog.kt. This contains the Dog data class that will be
used to represent the dog's photo, name, age, and hobbies. It also contains a list of dogs
and the information that you will use as the data in your app.
3. Open res > drawable. This contains all the image assets that you need for this project,
including the app icon, dog images, and icons.
4. Open res > values > strings.xml. This contains the strings you use in this app, including
the app name, dog names, their descriptions, and more.
5. Open MainActivity.kt. This contains the code to create a simple list that displays a photo
of a dog, the dog's name, and the dog's age.
7. DogItem() contains a Row that displays a photo of the dog and information about it.
10. WoofPreview() allows you to see a preview of the app in the Design pane.
In this codelab, you will be working with both light and dark themes, however, most of the
codelab is in light theme. Before you get started, ensure that your device/emulator is in light
47
theme.
48
In order to view your app in light theme, on your emulator or physical device:
Run the starter code to see what you're starting with; it's a list that displays dogs with their
photos, names, and ages. It is functional, but it doesn't look great, so we are going to fix that.
Add Color:
A color scheme is the combination of colors that your app uses. Different color combinations
evoke different moods, which influences how people feel when they use your app.
Color, in the Android system, is represented by a hexadecimal (hex) color value. A hex color
code starts with a pound (#) character, and is followed by six letters and/or numbers that
represent the red, green, and blue (RGB) components of that color. The first two letters/numbers
refer to red, the next two refer to green, and the last two refer to blue.
49
A color can also include an alpha value—letters and/or numbers—which represents the
transparency of the color (#00 is 0% opacity (fully transparent), #FF is 100% opacity (fully
opaque)). When included, the alpha value is the first two characters of the hex color code after
the pound (#)character. If an alpha value is not included, it is assumed to be #FF, which is 100%
opacity (fully opaque).
Add Shape:
Applying a shape can change so much about the look and feel of a composable. Shapes direct
attention, identify components, communicate state, and express brand. The Shape.kt file is
used to define shapes of components in Compose. There are three types of components: small,
medium, and large. In this section, you will modify the Card component, which is defined as
medium size.
Components are grouped into shape categories based on their size.
TalkBack is a Google screen reader that provides spoken feedback so users can navigate their
device without looking at the screen. This is especially helpful for people with impaired vision.
Once TalkBack is enabled, users can navigate their device through spoken feedback and
gestures— such as swipes and taps. Navigating with TalkBack is a great way for you to test for
areas of improvement in your app.
Switch Access lets you interact with your Android device using one or more switches instead
of the touchscreen. This alternative to using the touchscreen for users is especially helpful to
users with limited dexterity. Switch Access scans the items on your screen, highlighting each
item in turn, until you make a selection.
To use Switch Access, you'll first need one or more switches. There are several kinds of
switches, when Switch Access is enabled, there is a Menu tab at the top of the device's screen.
When selected, the tab opens a global menu with navigation options, such as Back and Home,
which are equivalent to the gestures on the device screen. Some options customize Switch
Access behavior.
Improving UI Accessibility
50
There are a number of UI design choices to consider when trying to create a more
accessible app. In addition to attributes and behaviors that allow for effective usage of
TalkBack and Switch Access, below are some UI optimizations you can make to improve the
accessibility of your app. Content description
Users of accessibility services, such as screen readers (like TalkBack), rely on content
descriptions to understand the meaning of elements in an interface. In some cases, such as
when information is conveyed graphically within an element, content descriptions can provide
a text description of the meaning or action associated with the element.
In Compose, you can describe visual elements using the contentDescription attribute. For
strictly decorative visual elements, it's okay to set the contentDescription to null.
Any on-screen element that someone can interact with must be large enough for reliable
interaction. The minimum touch target size for something clickable is 48dp high x 48dp wide.
There are a number of Material Design components for which Compose automatically assigns
the correct minimum target size. Keep in mind that the minimum touch target size refers to
clickable components smaller than 48dp. Components larger than 48dp will have a touch target
that is at least the size of the component.
51
MODULE 4
4.1.
ARCHITECTURE COMPONENTS[4]
4.1.1. Stages of Activity Life cycle
The activity lifecycle consists of the different states that an activity can go through, from
when the activity first initializes to its destruction, at which time the operating system (OS)
reclaims its memory. Typically, the entry point of a program is the main() method. Android
activities, however, begin with the onCreate() method; this method would be the equivalent of
the egg stage in the above example. You have used activities already, many times throughout
this course, and you might recognize the onCreate() method. As the user starts your app,
navigates between activities, navigates inside and outside of your app, the activity changes state.
An app's architecture provides guidelines to help you allocate the app responsibilities between
the classes. A well-designed app architecture helps you scale your app and extend it with
additional features. Architecture can also simplify team collaboration.
Separation of concerns
The separation of concerns design principle states that the app is divided into classes of
functions, each with separate responsibilities.
The drive UI from a model principle states that you should drive your UI from a model,
preferably a persistent model. Models are components responsible for handling the data for an
52
app. They're
53
independent from the UI elements and app components in your app, so they're unaffected by the
app's lifecycle and associated concerns.
Considering the common architectural principles mentioned in the previous section, each app
should have at least two layers:
• UI layer: a layer that displays the app data on the screen but is independent of the data.
• Data layer: a layer that stores, retrieves, and exposes the app data.
UI layer
The role of the UI layer, or presentation layer, is to display the application data on the screen.
Whenever the data changes due to a user interaction, such as pressing a button, the UI should
update to reflect the changes.
• UI elements: components that render the data on the screen. You build these elements
using Jetpack Compose.
• State holders: components that hold the data, expose it to the UI, and handle the app
logic. An example state holder is ViewModel.
ViewModel
The ViewModel component holds and exposes the state the UI consumes. The UI state is
application data transformed by ViewModel. ViewModel lets your app follow the architecture
principle of driving the UI from the model.
54
ViewModel stores the app-related data that isn't destroyed when the activity is destroyed and
recreated by the Android framework. Unlike the activity instance, ViewModel objects are not
destroyed. The app automatically retains ViewModel objects during configuration changes so
that the data they hold is immediately available after the recomposition.
To implement ViewModel in your app, extend the ViewModel class, which comes from the
architecture components library and stores app data within that class.
Fig.4.1.3 UI
1. Navigation Graph: Jetpack Compose utilizes a navigation graph to define the various
destinations and navigation paths within an application. The navigation graph is defined
using a composable function, specifying the navigation routes and associated
destinations. This declarative approach allows developers to visualize and manage the
app's navigation structure effectively.
2. NavHost Composable: The NavHost composable serves as the container for displaying
different destinations within the application. It is responsible for managing the
navigation stack and rendering the appropriate destination based on the current
navigation state. Developers can define a NavHost within their composables hierarchy to
enable navigation functionality.
through user
56
interactions such as button clicks or gestures. Jetpack Compose provides a type-safe API
for defining navigation actions, ensuring compile-time safety and preventing runtime
errors.
5. Safe Args: Jetpack Compose integrates seamlessly with Safe Args, a feature of the
Navigation component that generates type-safe accessors for navigation arguments. This
allows developers to pass data between composables safely and efficiently, reducing the
likelihood of runtime errors caused by type mismatches or missing arguments.
6. Deep Linking: Jetpack Compose supports deep linking, allowing developers to define
custom URL patterns that navigate to specific destinations within the application. Deep
linking enables seamless integration with external sources such as web links or
notifications, enhancing the overall user experience.
Overall, navigation in Jetpack Compose offers a modern and efficient approach to handling
navigation within Android applications, empowering developers to create intuitive and seamless
user experiences. With its declarative syntax, type-safe API, and seamless integration with other
Jetpack libraries, Jetpack Compose simplifies the navigation process while providing flexibility
and scalability for building complex navigation flows.
Adapting for different screen sizes in Android development using Kotlin is crucial for creating
apps that provide a consistent and user-friendly experience across various devices, ranging from
smartphones to tablets and beyond. Here's some content on this topic:
57
and position based on available screen space, ensuring optimal rendering on devices with
different screen sizes.
2. Resource Qualifiers:
Android's resource system supports qualifiers such as layout, drawable, values, and more,
allowing developers to provide alternative resources for different screen sizes. By creating
layout files, drawables, and dimensions specific to screen sizes (e.g., layout-large, drawable-
xhdpi, values-sw600dp), developers can tailor the app's UI elements to fit various screen
configurations.
Kotlin enables developers to implement responsive design patterns, such as flexible layouts,
scalable fonts, and adjustable margins/padding, to accommodate varying screen sizes.
Techniques like using ConstraintLayout guidelines, percent-based dimensions, and dynamic
resource loading help create UIs that adapt seamlessly to different screen dimensions.
4. ConstraintLayout Guidelines:
Kotlin supports testing frameworks like Espresso and UI Automator for validating app
behavior across different screen sizes and orientations. By conducting comprehensive testing on
various devices and screen configurations, developers can identify and address layout issues,
ensuring a seamless user experience across the board.
In conclusion, adapting for different screen sizes in Android development using Kotlin involves
leveraging responsive design techniques, resource qualifiers, modular UI components, and
testing methodologies to create apps that deliver a consistent and intuitive user experience
across diverse devices.
58
59
MODULE 5
CONNECT TO THE INTERNET
2. Replace the code with the following code for a program that shows a weather forecast of
sunny weather. In the main() function, first we print out the text: Weather forecast. Then
we print out: Sunny.
3. println() is a synchronous call because the task of printing the text to the output is
completed before execution can move to the next line of code. Because each function
call in main() is synchronous, the entire main() function is synchronous. Whether a
function is synchronous or asynchronous is determined by the parts that it's composed of.
4. A synchronous function returns only when its task is fully complete. So after the last
print statement in main() is executed, all work is done. The main() function returns and
the program ends.
Add a delay delay() is actually a special suspending function provided by the Kotlin coroutines
library. Execution of the main() function will suspend (or pause) at this point, and then resume
once the specified duration of the delay is over (one second in this case).
If you try to run your program at this point, there will be a compile error: Suspend function
'delay' should be called only from a coroutine or another suspend function.
For the purposes of learning coroutines within the Kotlin Playground, you can wrap your
existing code with a call to the runBlocking() function from the coroutines library.
runBlocking() runs an event loop, which can handle multiple tasks at once by continuing each
task where it left off when it's ready to be resumed. Suspending functions
60
A suspending function is like a regular function, but it can be suspended and resumed again
later. To do this, suspend functions can only be called from other suspend functions that make
this capability available.
A suspending function may contain zero or more suspension points. A suspension point is the
place within the function where execution of the function can suspend. Once execution resumes,
it picks up where it last left off in the code and proceeds with the rest of the function.
Retrofit creates a network API for the app based on the content from the web service. It fetches
data from the web service and routes it through a separate converter library that knows how to
decode the data and return it in the form of objects, like String. Retrofit includes built-in support
for popular data formats, such as XML and JSON. Retrofit ultimately creates the code to call
and consume this service for you, including critical details, such as running the requests on
background threads.
61
Fig.5.1.2 Retrofit
ViewModelScope
A viewModelScope is the built-in coroutine scope defined for each ViewModel in your app. Any
coroutine launched in this scope is automatically canceled if the ViewModel is cleared.
• In Kotlin, you can define a repository class that encapsulates the logic for fetching and
manipulating data. This class abstracts away the details of data retrieval and provides a
clean interface for interacting with data sources.
• For example, a UserRepository class might contain methods for fetching user data from
a local database or a remote server.
• In Kotlin, you can achieve manual dependency injection using constructor injection or
method injection.
62
Displaying a photo from a web URL might sound straightforward, but there is quite a
bit of engineering to make it work well. The image has to be downloaded, internally
stored(cached), and decoded from its compressed format to an image that Android can use.
You can cache the image to an in-memory cache, a storage-based cache, or both. All
this has to happen in low-priority background threads so the UI remains responsive. Also, for
the best network and CPU performance, you might want to fetch and decode more than one
image at once.
Fortunately, you can use a community-developed library called Coil to download, buffer,
decode, and cache your images. Without the use of Coil, you would have much more work to
do.
1. Open the Mars Photos solution app from the Add repository and Manual DI codelab.
2. Run the app to confirm that it shows the count of Mars photos retrieved.
4. In the dependencies section, add this line for the Coil library: // Coil
implementation("io.coil-kt:coil-compose:2.4.0")
5. Click Sync Now to rebuild the project with the new dependency.
64
MODULE 6
DATA
PERSISTENCE
In Kotlin, you're familiar with data types like Int and Boolean. SQLite databases use data types
too! Data table columns must have a specific data type. The following table maps common
Kotlin data types to their SQLite equivalents.
Int INTEGER
Boolean BOOLEAN
The tables in a database and the columns in each table are collectively known as the schema. In
the next section, you download the starter data set and learn more about its schema.
You read data from a SQLite database with a SELECT statement. A simple SELECT statement
consists of the SELECT keyword, followed by the column name, followed by the FROM
keyword, followed by the table name. Every SQL statement ends with a semicolon (.
65
66
A SELECT statement can also return data from multiple columns. You must separate column
names with a comma.
If you want to select every column from the table, you use the wildcard character (*) in place of
the column names.
In either case, a simple SELECT statement like this returns every row in the table. You just need
to specify the column names you want it to return.
6.1.3. Use SELECT statements with aggregate functions and distinct values
Reduce columns with aggregate functions
SQL statements aren't limited to returning rows. SQL offers a variety of functions that can
perform an operation or calculation on a specific column, such as finding the maximum value,
or counting the number of unique possible values for a particular column. These functions are
called aggregate functions. Instead of returning all the data of a specific column, you can return
a single value from a specific column.
• COUNT(): Returns the total number of rows that match the query.
• SUM(): Returns the sum of the values for all rows in the selected column.
• AVG(): Returns the mean value—average—of all the values in the selected column.
67
Instead of a column name, you can call an aggregate function and pass in a column name as an
argument between the parentheses.
Instead of returning that column's value for every row in the table, a single value is returned
from calling the aggregate function.
Aggregate functions can be an efficient way to perform calculations on a value when you don't
need to read all the data in a database. For example, you might want to find the average of the
values in a column without loading your entire database into a List and doing it manually.
After the table name, on a new line, you can add the WHERE keyword followed by an
expression. When writing more complex SQL queries, it's common to put each clause on a new
line for readability.
This query performs a boolean check for each selected row; if the check returns true, it includes
the row in the result of the query. Rows for which the query returns false are not included in
the result.
68
Kotlin provides an easy way to work with data through data classes. While it is easy to work
with in-memory data using data classes, when it comes to persisting data, you need to convert
this data into a format compatible with database storage. To do so, you need tables to store the
data and queries to access and modify the data.
• Room entities represent tables in your app's database. You use them to update the data
stored in rows in tables and to create new rows for insertion.
• Room DAOs provide methods that your app uses to retrieve, update, insert, and delete
data in the database.
• Room Database class is the database class that provides your app with instances of the
DAOs associated with that database.
You implement and learn more about these components later in the codelab. The
following diagram demonstrates how the components of Room work together to
interact with the database.
69
70
4. Use the booleanPreferencesKey() function to define a key and pass it the name
is_linear_layout. Similar to SQL table names, the key needs to use an underscore format.
This key is used to access a boolean value indicating whether the linear layout should be
shown.
Exception handling
Any time you interact with the file system on a device, it's possible that something can
fail. For example, a file might not exist, or the disk could be full or unmounted. As DataStore
reads and writes data from files, IOExceptions can occur when accessing the DataStore. You use
the catch{} operator to catch exceptions and handle these failures.
1. In the companion object, implement an immutable TAG string property to use for logging.
71
MODULE 7
WORK MANAGER
Work Manager:
WorkManager is part of Android Jetpack and an Architecture Component for background work
that needs a combination of opportunistic and guaranteed execution. Opportunistic execution
means that WorkManager does your background work as soon as it can. Guaranteed execution
means that WorkManager takes care of the logic to start your work under a variety of situations,
even if you navigate away from your app.
WorkManager is an incredibly flexible library that has many additional benefits. Some of these
benefits include:
• Support for constraints, such as network conditions, storage space, and charging status.
• Output from one work request used as input for the next.
The WorkManager library is a good choice for tasks that you need to complete. The running
of these tasks is not dependent on the app continuing to run after the work is enqueued. The
tasks run even if the app is closed or the user returns to the home screen.
72
WorkManager is one option for running a task off of the main thread but it is not a catch-all for
running every type of task off of the main thread.
• WorkManager: This class actually schedules your WorkRequest and makes it run. It
schedules a WorkRequest in a way that spreads out the load on system resources, while
honoring the constraints you specify.
A WorkInfo object contains details about the current state of a WorkRequest, including:
• If the WorkRequest is finished and any output data from the work.
These methods return LiveData. LiveData is a lifecycle aware observable data holder. We
convert it into a Flow of WorkInfo objects by calling .asFlow().
73
MODULE 8
COMPOSE WITH VIEWS
Creating Layout
When building an app with Views, you construct the UI inside of a Layout. Layouts
are typically declared using XML. These XML layout files are located in the
resources directory under res > layout. Layouts contain the components that make
up the UI; these components are known as Views.
XML syntax consists of tags, elements, and attributes. For more details on XML syntax,
reference the Create XML layouts for Android codelab.
In this section, you build an XML layout for the "Type of juice" entry dialog
pictured.
74
1. Create a new Layout Resource File in the main > res > layout directory called
fragment_entry_dialog.
75
The fragment_entry_dialog.xml layout contains the UI components that the app displays to the
user.
Notice that the Root element is a ConstraintLayout. This type of layout is a ViewGroup that lets
you position and size Views in a flexible way using the constraints. A ViewGroup is a type of
View that contains other Views, called children or child Views. The following steps cover this
topic in more detail, but you can learn more about ConstraintLayout in Build a Responsive UI
with ConstraintLayout.
View interoperability in Jetpack Compose refers to the seamless integration and coexistence of
Compose-based UI components with existing Android Views and ViewGroups. This capability
allows developers to gradually adopt Jetpack Compose within their Android projects while
leveraging the rich ecosystem of traditional Android Views when needed. Here's some content
on view interoperability in Jetpack Compose:
- Developers can embed Composables within existing layouts defined using XML-based
Views and ViewGroups, enabling incremental adoption of Compose in legacy Android projects.
2. ComposeView:
- Developers can include a ComposeView within their layout XML files and inflate
Composables dynamically at runtime, allowing for seamless integration of Compose-based UI
elements with Views-based layouts.
76
- Jetpack Compose provides APIs for hosting Composables within Views, enabling the
composition of UI elements using both Compose and traditional Android Views in the same
layout hierarchy.
- Developers can create custom Views that act as containers for Composables, facilitating
the reuse of existing View-based components alongside Compose-based UI elements.
4. Bi-directional Communication:
- Developers can pass data and events between Composables and Views using interfaces,
callbacks, or LiveData, enabling seamless interaction between the two paradigms within the
same application.
5. Gradual Migration:
- This approach enables teams to leverage the benefits of Compose, such as declarative UI
design and state management, while maintaining compatibility with legacy Views during the
transition period.
6. Performance Considerations:
- Excessive nesting of Composables within Views or vice versa may impact rendering
performance and hinder the benefits of using Jetpack Compose's optimized rendering pipeline.
7. Best Practices:
- When incorporating view interoperability, developers should follow best practices for
managing state, handling layout transitions, and optimizing rendering performance.
- Clear documentation and code organization are essential to ensure maintainability and
readability, especially in projects with a mix of Composables and traditional Android Views.
77
78
9.Case Study
Scenario:
The transportation system for schools in cities has become complex, especially in ensuring the
safety and convenience of students. Parents and guardians are often worried about knowing the
exact location of school buses in real time. Traditional methods of communication can
sometimes fail, leading to stress and delays in tracking buses. Therefore, there is a need for a
reliable mobile application that provides real-time tracking of school buses.
Problem Statement:
Parents and school authorities face challenges in tracking the precise location of school buses in
real time. Current systems are either inaccurate, lack real-time updates, or require significant
manual effort. The absence of a dynamic and user-friendly solution increases stress and delays,
especially during emergencies or when a bus is running late.
Solution:
To address this problem, a mobile application was developed that enables real-time tracking of
school buses using GPS. The app has two components:
Host app: Installed on the driver’s device to send the real-time location to a Firebase
database.
Client app: Used by parents or guardians to track the bus location, view the predefined
bus route, and monitor the bus's progress in real time. The integration of Google Maps
and Firebase ensures accurate location tracking and real-time updates, helping parents
stay informed.
Design
The app’s design focused on providing a simple and intuitive interface. The user-friendly
interface ensures that both the driver (using the host app) and parents (using the client app) can
easily interact with the app without technical difficulties. The main features are the map, which
shows the current location of the bus, the path already covered, and the remaining route.
79
Android Development
80
• Host App: Captures the GPS location of the bus and sends it to Firebase in real time.
• Client App: Fetches location data from Firebase, draws the predefined route, and shows
the current location of the bus on Google Maps. It also calculates the distance covered and
highlights the remaining route.
Both apps use Android’s FusedLocationProviderClient to handle location services, and Google
Maps API to display routes and the bus's current location.
Firebase services were integrated for real-time data storage and syncing:
• Firebase Realtime Database: Stores the location data provided by the bus.
• Firebase Authentication: Provides secure sign-in and sign-out functionality for users.
The data in Firebase is continuously updated and fetched in real time to ensure that the client app
shows the most recent location of the bus.
• Unit Testing: Individual features like location updates, Google Maps integration, and
Firebase data syncing were tested.
• User Acceptance Testing (UAT): A small group of parents and bus drivers tested the
app to ensure it met real-world requirements.
• Iterative Improvements: Based on feedback from UAT, changes
were made to the app's UI, performance optimization, and bug fixing.
81
The final version of the app was deployed for users on the Indus App Store for parents, school
authorities, and bus drivers. Firebase Realtime Database is continuously monitored to ensure that
location data is uploaded and fetched without delays.
Conclusion
This project successfully addressed the issue of real-time school bus tracking by creating a robust
mobile solution. Through a combination of Google Maps integration and Firebase services, the app
ensures that parents can view the bus's exact location and progress on a predefined route. This
solution improves safety, reduces stress, and provides peace of mind to parents and school
authorities alike.
82
Conclusion
The internship provided a structured curriculum that equipped interns with essential skills
and knowledge, ensuring they emerged proficient in developing professional-grade Android
applications. From foundational concepts to advanced topics, interns gained valuable hands-on
experience and emerged ready to embark on successful careers as Android developers.
83
References
84