Google Android Dev Final
Google Android Dev Final
Bachelor of Technology
in
Electronics and Communication Engineering
by
August 2025
I
Electronics and Communication Engineering Department
CERTIFICATE
This is to certify that the Intern-I titled GOOGLE ANDROID DEVELOPER VIRTUAL
INTERNSHIP bonafide record of the work done by SARIPALLI SANJANA (322103312188) in
partial fulfillment of the requirements for the award of the degree of Bachelor of Technology in
II
CERTIFICATE
322103312188
III
ACKNOWLEDGEMENT
We would like to express our deep sense of gratitude to our esteemed institute
Gayatri Vidya Parishad College of Engineering (Autonomous), which has provided
us an opportunity to fulfil our cherished desire.
We are highly indebted to Dr. N. Deepika Rani, Associate Professor and Head
of the Department of Electronics and Communication Engineering, Gayatri Vidya
Parishad College of Engineering (Autonomous), for giving us an opportunity to do the
internship in college.
We would also like to thank our Internship Coordinator Dr.Ch. Sitha Kumari,
Associate Professor and for the kind suggestions and guidance for the successful
completion of our internship.
We are grateful to AICTE EDUSKILLS and GOOGLE for providing us with this
learning opportunity. Finally, we are indebted to the teaching and non-teaching staff of the
Electronics and Communication Engineering Department for all their support in completing
our project.
SARIPALLI SANJANA
(322103312188)
IV
ABSTRACT
V
INTRODUCTION ON GOOGLE ANDROID DEVELOPMENT
VI
CONTENTS
Page No.
Certificate III
Acknowledgment IV
Abstract V
Introduction VI
Contents VII-VIII
VII
Connect
5. to the internet 38 - 41
5.1. Get data from the internet
5.2. Load and display images from the Internet
Data
6. Persistence 42- 46
6.1. Introduction to SQL
6.2. Use Room for data persistence
Work
7. Manager 47 - 48
7.1. Schedule tasks with Work Manager
Views
8. and Compose 49 - 52
8.1. Android Views
8.2. Views in Compose
Case
9. Study 53 - 55
Conclusion 56
References 57
List of Figures
Figure No Caption (Name of the figure)
3.4 Collections
3.5 Lists
4.2 Layers
IX
1
4.3 Adaptive to different screen sizes
X
2
ANDROID BASICS WITH COMPOSE
MODULE 1 - Your first Android app
1
Parts of function
A function is a segment of a program that performs a specific task. Your program may have one
or more functions.
Define versus call a function
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
These are the key parts needed to define a function:
The function needs a name, so you can call it later.
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.
2
Kotlin style guide
Throughout this course, you'll learn about good coding practices to follow as an Android
developer. One such practice is to follow Google's Android coding standards for code written in Kotlin.
The complete guide is called a style guide and explains how code should be formatted in terms of
visual appearance and the conventions to follow when writing your code. For example, the style guide
includes recommendations on use of whitespace, indentation, naming, and more.
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.
Each statement should be on its own line.
The opening curly brace should appear at the end of the line where the function begins.
There should be a space before the opening curly brace.
3
Set Up Android Studio
1.2.1. Download and Install Android Studio
Download Android Studio
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.
3. Read the License Agreement.
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.
5. Click Download Android Studio to start the download.
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!
Install Android Studio on Windows
1. Open the folder where you downloaded and saved the Android Studio installation file.
2. Double-click the downloaded file.
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.
4
4. Click Next to start the installation.
5. Accept the default installation settings for all steps.
6. Click Finish when the installation is done to launch Android Studio.
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.
10. When the download and installation completes, click Finish.
The Welcome to Android Studio window displays and you're ready to start creating apps.
5
3. 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:
4. You may see a What's New pane which contains updates on new features in Android
Studio. Close it for now.
5. 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.
6. After pressing Split you should see three areas: The Project view (1) shows the files and
folders of your project The Code view (2) is where you edit code The Design view (3) is
where you preview what your app looks like
7. In the Design view, you will see a blank pane with this text:
8. 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.
6
1.2.3. Run your first App on Android Emulator
In this task, you'll use the Device Manager to create an Android Virtual Device (AVD). An AVD
is a software version, also called an emulator, of a mobile device that runs on your computer and mimics
the configuration of a particular type of Android device. This could be any phone, tablet, TV, watch, or
Android auto device. You'll use the AVD to run the Greeting Card app.
2. Click .
7
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.
8
1.2.4. How to connect your Android Device
1.2.4.1. Enable USB Debugging
To let Android Studio communicate with your Android device, you must enable USBdebugging in the
Developer options settings of the device.
To show developer options and enable USB debugging:
1. On your Android device, tap Settings > About phone.
2. Tap Build number seven times.
3. If prompted, enter your device password or pin. You know you succeeded when you see a
You are now a developer! message.
10
Fig1.9- Running app on android device with a cable
11
Jetpack Compose
Jetpack Compose is a modern toolkit for building Android UIs. Compose simplifies and accelerates UI
development on Android with less code, powerful tools, and intuitive Kotlin capabilities. With
Compose, you can build your UI by defining a set of functions, called composable functions, that take in
data and describe UI elements.
Composable functions
Composable functions are the basic building block of a UI in Compose. A composable function:
Describes some part of your UI.
Doesn't return anything.
Takes some input and generates what's shown on the screen.
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.
12
1.3.2. Add images to your Android App
1. In Android Studio, click View > Tool Windows > Resource Manager or click the
Resource Manager tab next to the Project window.
2. Click + (Add resources to the module) > Import Drawables.
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.
4. This action opens the Import drawables dialog.
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.
6. Select No Density from the VALUE list.
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.
11. Click app > res > drawable to confirm that the image is in the drawable folder.
13
MODULE 2 - Building App UI
2.1. Kotlin Fundamentals
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:
14
Use a When statement for multiple branches:
In Kotlin, when you deal with multiple branches, you can use the when statement instead of the
if/else statement because it improves readability, which refers to how easy it is for human
readers, typically developers, to read the code. It's very important to consider readability when
youwrite your code because it's likely that other developers need to review and modify your code
throughout its lifetime. Good readability ensures that developers can correctly understand your
code and don't inadvertently introduce bugs into it.
when statements are preferred when there are more than two branches to consider.
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.
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.
15
2.1.2. Use classes and objects in Kotlin
A class definition starts with the class keyword, followed by a name and a set of curly braces.
The part of the syntax before the opening curly brace is also referred to as the class header. In the
curly braces, you can specify properties and functions for the class. You learn about properties
and functions soon. You can see the syntax of a class definition in this diagram:
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. You can see the syntax in this
diagram:
16
Defining class properties:
While methods define the actions that a class can perform, the properties define the class's
characteristics or data attributes. For example, a smart device has these properties:
Name. Name of the device.
Category. Type of smart device, such as entertainment, utility, or cooking.
Device status. Whether the device is on, off, online, or ofline. The device is considered
online when it's connected to the internet. Otherwise, it's considered ofline.
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
immutable property with the val keyword and a mutable property with the var keyword.
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
In the SmartDevice class, the name and category properties are immutable. You need to ensure
that all the instances of the SmartDevice class initialize the name and category properties. With
the current implementation, the values for the name and category properties are hardcoded. This
means that all the smart devices are named with the "Android TV" string and categorized with
the "Entertainment" string.
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:
17
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 primaryconstructor, 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 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.
Secondary constructor body. Initialization of the primary constructor is followed by a
set of curly braces, which contain the secondary constructor's body.
You can see the syntax in this diagram:
18
2.2. Add a button to an App
2.2.1. Creating an interactive Dice Roller App
1. In Android Studio, click File > New > New Project.
2. In the New Project dialog, select Empty Activity and then click Next.
19
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.
Create a vertical layout:
20
This ensures that the children within the column are centered on the device screen with respect to
the width.
Add a Button:
value. res/values/strings.xml
<string name="roll">Roll</string>
Note: To import the Button composable, use the following statement: import
androidx.compose.material3.Button.
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 theresult to the Text composable.
Build the Dice roll logic:
Now that all the necessary composables are present, you modify the app so that a tap of the button
rolls the dice.
Make the button interactive
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 asa 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".
A lambda is a function literal, which is a function like any other, but instead of being declared
separately with the fun keyword, it is written inline and passed as an expression. The
Button composable is expecting a function to be passed as the onClick parameter. This is
theperfect place to use a lambda, and you will be writing the lambda body in this section.
3. In the Button() function, remove the /*TODO*/ comment from the value of the
lambdabody of the onClick parameter.
4. A dice roll is random. To reflect that in code, you need to use the correct syntax to
generatea random number. In Kotlin,you can use the random() method on a number
21
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.
State in an app is any value that can change over time. In this app, the state is the bill amount.
Add a variable to store state:
1. At the beginning of the EditNumberField() function, use the val keyword to add
an amountInput variable set it to "0" value:
val amountInput = "0"
2. Set the value named parameter to an amountInput valueheck the preview. The
text boxdisplays the value set to the state variable as you can see in this image:
4. 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.
Testing, for software, is a structured method of checking your software to make sure that
it works as expected. Automated testing is code that checks to ensure that another piece of code
that you wrote works correctly.
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 introduced.
22
Why automated tests are important
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.
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.
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. The test APK then
runs its tests against the app APK.[2]
23
MODULE 3 - DISPLAYLISTS AND USE MATERIAL DESIGN
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.
24
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.
You refer to enum constants using the dot operator.
Kotlin Arrays:
An array is the simplest way to group an arbitrary number of values in your programs.
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.
24
In the device's memory, elements in the array are stored next to each other. While the
underlying details are beyond the scope of this codelab, this has two important implications:
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.
25
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.
List and MutableList
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.
So what do List and MutableList do?
List is an interface that defines properties and methods related to a read-only
orderedcollection 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
array- based 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.
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.
26
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.
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.
27
3.2 Build a Scrollable List
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.
28
Name the new class Affirmation and make it a Data class.
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.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.
5. In the com.example.affirmations.data package, open the Datasource.kt file and
uncomment the two import statements and the contents of the Datasource class.
29
3.3. Build beautiful Apps
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 theme.
In order to view your app in light theme, on your emulator or physical device:
1. Go to the Settings app on the device.
2. Search for Dark theme and click into it.
3. If Dark theme is on, switch it off.
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.
30
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.
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.
31
3.2.2. Testing for Accessibility
Use woof with Talk Back
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:
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.
Touch target size:
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.
32
MODULE 4 – NAVIGATION AND APP ARCHITECTURE
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.
The separation of concerns design principle states that the app is divided into classes of
functions, each with separate responsibilities.
Drive UI from a model
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
app. They're independent from the UI elements and app components in your app, so they're
unaffected by the app's lifecycle and associated concerns.
Recommended app architecture
Considering the common architectural principles mentioned in the previous section, each app
33
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.
The UI layer is made up of the following components:
UI elements: components that render the data on the screen. You build
these elementsusing 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.
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.
34
4.2. Navigation in Jetpack Compose
Navigation in Jetpack Compose simplifies the process of implementing navigation within
Android applications, offering a declarative and type-safe approach to handle navigation
between composables. By integrating with the Navigation component of the Android Jetpack
library, Jetpack Compose provides developers with powerful tools to manage navigation flows
efficiently.
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.
3. Navigation Actions: Navigation actions define the transitions between destinations within
the navigation graph. Actions can be triggered programmatically or through user
interactions such as button clicks or gestures. Jetpack Compose provides a type-safe API
for defining navigation actions.
4. Navigation Composables: Jetpack Compose offers a set of navigation-related
composables to facilitate navigation within the application. These include functions for
navigating to specific destinations, popping destinations off the navigation stack, and
accessing navigation arguments passed between composables.
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.
7. ViewModel Integration: Navigation in Jetpack Compose can be combined with
ViewModels to manage the navigation state and business logic of the application. By
separating concerns and adhering to the MVVM (Model-View-ViewModel) architecture,
developers can create robust and maintainable navigation flows within their
applications.
Overall, navigation in Jetpack Compose offers a modern and efficient approach to handling
35
navigation within Android applications, empowering developers to create intuitive and seamless
36
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.
4.3.Adapt for different screen sizes
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:
1. Layouts and Views:
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.
3. Responsive Design Patterns:
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's ConstraintLayout offers guidelines that enable developers to define flexible constraints
for UI elements, ensuring consistent spacing and alignment across different screen sizes. By
anchoring UI elements to guidelines and constraints, developers can create layouts that adapt
fluidly to varying screen dimensions without sacrificing design integrity.
5. Testing and Validation:
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.[1]
37
38
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 issynchronous 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
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.
39
1.1.2. Introduction to Web services
Create a layer for the network service that communicates with the backend server and fetches the
required data. You use a third-party library, called Retrofit, to implement this task. You learn
more about this later. The ViewModel communicates with the data layer, and the rest of the app
is transparent to this implementation.
You use the Retrofit library to talk to the Mars web service and display the raw JSON response
as a String. The placeholder Text either displays the returned JSON response string or a message
indicating a connection error.
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.[4]
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.
40
1.2. Load and display images from the internet
The repository pattern serves as an intermediary between the data source (e.g.,
localdatabase, network) and the rest of the application.
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.
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.
[2]
Coil basically needs two things:
The URL of the image you want to load and display.
An AsyncImage composable to actually display that image.
41
Add Coil dependency
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.
42
MODULE 6 – DATA PERSISTENCE
6.1 Introduction to SQL
SQL Lite: SQLite is a commonly used relational database. Specifically, SQLite refers to a
lightweight C library for relational database management with Structured Query Language,
known as SQL and sometimes pronounced as "sequel" for short.
Representing data with SQLite
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.
43
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.
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.
Examples of SQL aggregate functions include the following:
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.
MIN(): Returns the smallest value in the selected column.
MAX(): Returns the largest value in the selected column.
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.
44
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.
Many email apps offer the feature to filter the messages shown based on certain criteria, such as
data, search term, folder, sender, etc. For these types of use cases, you can add a WHERE clause
to your SELECT query.
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.
45
6.2 Use Room for data persistence
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.
The following three components of Room make these workflows seamless.
Room entities represent tables in your app's database. You use them to update
the datastored 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 datain the database.
Room Database class is the database class that provides your app with instances
of theDAOs 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.
46
6.3 Store and access data using Keys with DataStore
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.
2. Preferences DataStore throws an IOException when an error is encountered while
reading data. In the isLinearLayout initialization block, before map(), use the
catch{}operator to catch the IOException.
3. Preferences DataStore throws an IOException when an error is encountered while
reading data. In the isLinearLayout initialization block, before map(), use the
catch{}operator to catch the IOException.[3]
47
MODULE 7 – WORK MANAGER
7.1 Schedule Tasks with 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 both asynchronous one-off and periodic tasks.
Support for constraints, such as network conditions, storage space, and charging status.
Chaining of complex work requests, such as running work in parallel.
Output from one work request used as input for the next.
Handling API-level compatibility back to API level 14 (see note).
Working with or without Google Play services.
Following system health best practices.
Support to easily display state of work requests in the app's UI.
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.
Some examples of tasks that are a good use of WorkManager:
Periodically querying for latest news stories.
Applying filters to an image and then saving the image.
Periodically syncing local data with the network.
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.
48
Work Manager Basics:
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().
49
MODULE 8 – COMPOSE WITH VIEWS
8.1 Android Views and Compose in 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.
1. Create a new Layout Resource File in the main > res > layout
directory called fragment_entry_dialog.
50
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.
51
8.2 Views in Compose
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:
a) 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 layouthierarchy.
b) 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:
52
b. This approach enables teams to leverage the benefits of Compose, such as
declarative UI designand state management, while maintaining compatibility with
legacy Views during the transition period.
6. Performance Considerations:
a) When incorporating view interoperability, developers should follow best practices for
managing state, handling layout transitions, and optimizing rendering performance.
b) Clear documentation and code organization are essential to ensure maintainability and
readability, especially in projects with a mix of Composables and traditional Android
Views.
In summary, view interoperability in Jetpack Compose offers developers the flexibility to combine
Composables with existing Views and ViewGroups, enabling gradual adoption of the Compose
framework within Android projects. By leveraging interoperability APIs and best practices,
developers can seamlessly integrate Compose-based UI components with traditional Android
Views, facilitating a smooth transition to modern UI development paradigms.[1]
53
9. Case Study
Problem Statement:
Many people rely on accurate weather forecasts to plan their daily activities, travel plans, and outdoor
events. However, finding a reliable, user-friendly weather forecasting app that provides realtime updates
and detailed information can be challenging. Users often face issues such as inaccurate forecasts,
complex user interfaces, and limited customization options. This case study focuses on developing a
user-friendly Android application called Weather Forecast that addresses these challenges and provides
an enhanced weather forecasting experience for users.
Solution:
Weather Forecast's solution entails utilizing multiple reliable data sources for accurate forecasts, creating
a minimalist and intuitive interface, implementing push notifications for real-time updates, and
integrating GPS for location-based forecasts. Customization options, including units, themes, and
notification settings, enhance user experience. Continuous updates and user feedback integration ensure
Weather Forecast remains a reliable and user-friendly weather forecasting
app.
Working Process:
1. User Research: The development team conducts extensive user research to understand user
preferences, motivations, and pain points. Insights from user surveys, interviews, and app analytics guide
the design of gamification features.
2. Gamification Design: Based on user feedback and industry best practices, the team identifies key
gamification elements to integrate into the app. These include achievement badges, progress tracking,
challenges, social sharing, and rewards.
3. Android Development: Using Android Studio and Kotlin programming language, the team redesigns
the app's user interface to accommodate gamification features seamlessly. They leverage Android
Jetpack components to implement functionality such as leaderboards, notifications, and in-app
messaging.
4. Integration of Firebase Services: Firebase Authentication, Realtime Database, and Cloud Messaging
services are integrated into the app architecture to enable real-time data synchronization and cloud-based
storage. This facilitates seamless user experiences across multiple devices and platforms.
54
5. Testing and Iteration: The development team conducts rigorous testing, including functional testing,
usability testing, and A/B testing of gamification features. User feedback and performance metrics guide
iterative refinements to optimize user experience and engagement.
Conclusion:
Weather Forecast exemplifies a user-centric approach to developing an Android weather forecasting
application, addressing the challenges users face with existing apps. By prioritizing accuracy, user-
friendliness, customization, and real-time updates, Weather Forecast aims to provide a seamless and
enhanced weather forecasting experience, empowering users to make informed decisions and plan their
activities with confidence.
55
56
10. CONCLUSION
The Google Android Development internship provided a comprehensive learning
experience in building Android applications, covering essential aspects from creating the first
app to incorporating advanced features like data persistence and background processing.
Throughout the internship, I gained proficiency in developing user interfaces following
Material Design principles, enabling me to create visually appealing and intuitive apps.
Additionally, the emphasis on displaying lists efficiently enhanced my ability to present data
effectively within applications. Starting with the creation of their first Android app, interns
progressed through building intuitive user interfaces, implementing Material Design principles,
structuring app navigation and architecture, integrating internet connectivity, managing data
persistence, and mastering background task processing.
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.
Additionally, the internship fostered a collaborative learning environment where
participants engaged in practical exercises, code reviews, and projects, allowing them to apply
theoretical knowledge to real-world scenarios. Through mentorship and peer interaction, interns
not only honed their technical skills but also developed critical problem-solving abilities and
effective communication within a team setting. Furthermore, the internship encouraged
continuous learning and adaptation to the ever-evolving landscape of Android development,
instilling in interns the mindset of lifelong learners equipped to tackle new challenges and
innovations in the field.
In conclusion, the Google Android Development internship provided a well-rounded
education in Android app development, covering fundamental concepts as well as advanced
techniques. The hands-on experience gained throughout the program has equipped me with the
skills necessary to create impactful and feature-rich Android applications.
57
11.REFERENCES