Choose the most correct answer and write its letter in the answer booklet provided.
(1 Mark Each)
i. All layout classes are the subclasses of: -
A. Android.widget
B. Android.view.View
C. Android.view.ViewGroup
D. Android.view.Widget
Answer (Click to show)
C. Android.view.ViewGroup
Explanation: ViewGroup is the base class for layouts and containers in Android. It extends View and provides the structure for holding other Views. All layout classes (LinearLayout, RelativeLayout, ConstraintLayout, etc.) are subclasses of ViewGroup.
ii. What is manifest XML in android?
A. A file that contains all information about the layout of the android application.
B. A file that contains all the information about the android application.
C. A file that contains all the information about the activities of the android application.
D. A file that contains all resources of the android application.
Answer (Click to show)
B. A file that contains all the information about the android application.
Explanation: AndroidManifest.xml is the essential configuration file that declares application components (activities, services, broadcast receivers, content providers), required permissions, hardware/software features, API level requirements, and other metadata about the application.
iii. What is the use of a content provider in Android?
A. For sharing data between one activity and another activity.
B. For storing data in a SQLite database.
C. For sending data from one application to another application.
D. For storing data in a shared preferences.
Answer (Click to show)
C. For sending data from one application to another application.
Explanation: Content providers manage access to a structured set of data. They encapsulate data and provide mechanisms for defining data security. Content providers are primarily used to share data between different applications in a safe and controlled manner.
iv. While developing android application developers can test their apps on: -
A. Emulators in android SDK.
B. Android phone.
C. Third-party emulator.
D. All of the above.
Answer (Click to show)
D. All of the above.
Explanation: Android developers can test applications on multiple platforms including: Android SDK emulators (AVD - Android Virtual Devices), physical Android phones/devices connected via USB, and third-party emulators like Genymotion or BlueStacks.
v. The services in android can be stopped using which of the following method?
A. system.exit() and stopService().
B. stopSelf() and stopService().
C. finish() and end().
D. serviceStop() and endService().
Answer (Click to show)
B. stopSelf() and stopService().
Explanation: Services can be stopped in two ways: stopSelf() is called by the service itself to terminate its own execution, while stopService() is called by external components (like activities) to request the service to stop.
vi. Identify among the following which is not a state in the service lifecycle.
A. Running.
B. Start.
C. Paused.
D. Destroyed.
Answer (Click to show)
C. Paused.
Explanation: The service lifecycle includes onCreate(), onStartCommand(), onBind(), and onDestroy() methods. Unlike activities, services do not have a “paused” state. Services are either running (started) or destroyed. The paused state is part of the activity lifecycle, not service lifecycle.
vii. Identify the dialogue class in Android among the following.
A. DatePicker.
B. AlertDialog.
C. ProgressDialog.
D. All of the above.
Answer (Click to show)
D. All of the above.
Explanation: Android provides several dialog classes including: AlertDialog (for showing alerts with buttons), DatePickerDialog (for date selection), ProgressDialog (for showing progress indicators), TimePickerDialog, and custom dialogs. All are subclasses of Dialog.
viii. What is the life cycle of broadcast receivers in android?
A. send intentO.
B. onReceive.
C. implicitBroadcastO.
D. sendBroadcastO, sendOrderBroadcastO, and sendStickyBroadcastO.
Answer (Click to show)
B. onReceive.
Explanation: Broadcast receivers have a single callback method: onReceive(). When a broadcast arrives, the system calls onReceive() and the BroadcastReceiver object is valid only during the execution of this method. Once onReceive() returns, the receiver is no longer active.
ix. What runs in the background and doesn’t have any UI components?
A. Intents.
B. Content Providers.
C. Services.
D. Applications.
Answer (Click to show)
C. Services.
Explanation: Services are application components that perform long-running operations in the background without providing a user interface. They can run even when the user is not interacting with the application.
x. Which one is not a nickname of a version of Android?
A. Cupcake.
B. Gingerbread.
C. Honeycomb.
D. Android.
Answer (Click to show)
D. Android.
Explanation: Android versions are named after desserts in alphabetical order: Cupcake (1.5), Donut (1.6), Eclair (2.0-2.1), Froyo (2.2), Gingerbread (2.3), Honeycomb (3.0-3.2), Ice Cream Sandwich (4.0), Jelly Bean (4.1-4.3), KitKat (4.4), Lollipop (5.0-5.1), Marshmallow (6.0), Nougat (7.0-7.1), Oreo (8.0-8.1), Pie (9.0), Android 10, etc. “Android” is the platform name, not a specific version nickname.
Question Two
Write true if the statement is correct and false if the statement is incorrect.
(10 Marks - 1 Mark Each)
(a) Mobile Apps On Both Android And IOS Platforms Should Not Perform Long Lasting Tasks, Such As Network.
Answer (Click to show)
TRUE
Explanation: Mobile apps should not perform long-lasting tasks like network operations on the main/UI thread as it can block the user interface and cause ANR (Application Not Responding) errors. Such operations should be performed on background threads using AsyncTask, Threads, WorkManager, or background services.
(b) Android is built upon the Java Micro Edition (J2ME) version of java.
Answer (Click to show)
FALSE
Explanation: Android is not built upon J2ME. Android uses its own Dalvik Virtual Machine (later replaced by Android Runtime - ART) which runs Dalvik bytecode converted from Java bytecode. Android’s core libraries are based on Apache Harmony and are different from J2ME.
(c) Native libraries is one of the core component of the .apk in android.
Answer (Click to show)
TRUE
Explanation: Native libraries are indeed one of the core components of an APK (Android Package Kit). They are typically located in the /lib folder of the APK and contain platform-specific compiled code written in C/C++ used by the application.
(d) The code which is compiled to run the android application is always contained within the xml layout file.
Answer (Click to show)
FALSE
Explanation: The compiled code (bytecode) is contained in DEX (Dalvik Executable) files, not in XML layout files. XML layout files define the user interface structure and are compiled into binary XML format, but the actual application logic is compiled into .dex files.
(e) The xml file that contains all the texts that your application uses is called text.xml.
Answer (Click to show)
FALSE
Explanation: The XML file that contains all text strings used in the application is called strings.xml, not text.xml. It is located in the res/values/ folder and contains string resources that can be localized and referenced throughout the app.
(f) There is no guarantee that an activity will be stopped prior to being destroyed.
Answer (Click to show)
TRUE
Explanation: Under memory pressure, the system may kill an application process directly without calling the activity’s onStop() method. This can happen when the system needs to reclaim memory quickly. Therefore, it’s important to save critical data in onPause() rather than relying solely on onStop().
(g) In an explicit intent, the sender specifies the type of receiver.
Answer (Click to show)
FALSE
Explanation: In an explicit intent, the sender specifies the exact component (class name) to be launched, not just the type. For example: new Intent(this, TargetActivity.class). The type of receiver is specified in implicit intents using actions, categories, and data.
(h) There can be only one running activity in a given time.
Answer (Click to show)
TRUE
Explanation: In Android, only one activity can be in the foreground (running/resumed state) at any given time. Multiple activities can be in paused or stopped states in the back stack, but only one activity is actively interacting with the user.
(i) Java is the only programming language used to make an android application.
Answer (Click to show)
FALSE
Explanation: Android applications can be developed using multiple programming languages including: Java, Kotlin (officially supported and recommended), C/C++ (via NDK for native code), and other JVM languages. Kotlin is now Google’s preferred language for Android development.
(j) The only database that android application can work with is SQLite.
Answer (Click to show)
FALSE
Explanation: While SQLite is the built-in database engine in Android, applications can work with various other databases including: Room Persistence Library (abstraction over SQLite), Realm, Firebase Realtime Database, Cloud Firestore, MySQL, PostgreSQL (via network connections), and other NoSQL databases.
Question Three
(a) Assume you have a running application that is already running an Activity called Activity1. Activity1 starts another Activity called Activity2. Name one Activity lifecycle method that will be called on Activity1 after this point, but before Activity2 starts.(2 Marks)
Answer (Click to show)
onPause()
Explanation: When Activity1 starts Activity2, the following sequence occurs:
Activity1’s onPause() method is called first
Activity2’s onCreate(), onStart(), and onResume() methods are called
Activity1’s onStop() is called only after Activity2 becomes visible
Therefore, onPause() is the lifecycle method called on Activity1 before Activity2 starts.
(b) Suppose you have an application that is running an Activity called Activity1. Suppose that Activity1 executes and starts other Activities, but that the user never quits or backs out of the Activity. How many times can Activity1’s onCreate() method get called?(2 Marks)
Answer (Click to show)
Multiple times
Explanation: Activity1’s onCreate() can be called multiple times even if the user never explicitly quits or backs out. This happens because:
Configuration changes: When the device rotates, changes language, or keyboard availability changes, the activity is destroyed and recreated by default
Memory pressure: The system may kill the activity when it’s in the background to reclaim memory, then recreate it when the user returns
“Don’t Keep Activities”: If this developer option is enabled, activities are destroyed as soon as they’re stopped
Each recreation calls onCreate() again.
(c) Suppose that there are two activities in an application named ActivityOne and ActivityTwo. You want to invoke ActivityTwo from ActivityOne. What code you will write?(3 Marks)
Answer (Click to show)
Code to invoke ActivityTwo from ActivityOne:
// Method 1: Simple intent with explicit classIntent intent = new Intent(ActivityOne.this, ActivityTwo.class);startActivity(intent);// Method 2: With action (if declared in manifest)Intent intent = new Intent("com.example.ACTION_VIEW_ACTIVITY_TWO");startActivity(intent);// Method 3: Using context and classIntent intent = new Intent(getApplicationContext(), ActivityTwo.class);startActivity(intent);
The intent must be declared in AndroidManifest.xml for ActivityTwo:
<activity android:name=".ActivityTwo" />
(d) How will you reference a textbox control in java file, that is available in XML file and the ID is txtName.(4 Marks)
Answer (Click to show)
Referencing a TextView/EditText with ID txtName in Java:
// In your Activity classpublic class MainActivity extends AppCompatActivity { // Declare the TextView variable private TextView txtName; // or EditText if it's an input field @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Set the layout first // Reference the textbox using findViewById txtName = findViewById(R.id.txtName); // Now you can use the textbox txtName.setText("Hello World"); // Set text String text = txtName.getText().toString(); // Get text txtName.setVisibility(View.VISIBLE); // Set visibility }}
(e) Suppose that there are two activities in an application named FirstActivity and SecondActivity. You want to send website name from ActivityOne to ActivityTwo. What code you will write? Suppose that website name is www.udom.ac.tz.(5 Marks)
Answer (Click to show)
Sending website name from FirstActivity to SecondActivity:
In FirstActivity.java:
public class FirstActivity extends AppCompatActivity { private String websiteName = "www.udom.ac.tz"; // Method to start SecondActivity with data private void openSecondActivity() { // Create Intent Intent intent = new Intent(FirstActivity.this, SecondActivity.class); // Method 1: Using putExtra intent.putExtra("website_key", websiteName); // Method 2: Using Bundle Bundle bundle = new Bundle(); bundle.putString("website_key", websiteName); intent.putExtras(bundle); // Method 3: Multiple extras intent.putExtra("website_name", websiteName) .putExtra("timestamp", System.currentTimeMillis()); // Start the activity startActivity(intent); } // Example button click handler public void onButtonClick(View view) { openSecondActivity(); }}
(f) How will you get the data in secondActivity? Refer to part (e).(4 Marks)
Answer (Click to show)
Receiving website name in SecondActivity:
In SecondActivity.java:
public class SecondActivity extends AppCompatActivity { private TextView displayTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); displayTextView = findViewById(R.id.displayText); // Get the intent that started this activity Intent receivedIntent = getIntent(); // Method 1: Get data using getStringExtra() String websiteName = receivedIntent.getStringExtra("website_key"); // Method 2: Get from Bundle Bundle bundle = receivedIntent.getExtras(); if (bundle != null) { websiteName = bundle.getString("website_key"); } // Method 3: With default value if key doesn't exist websiteName = receivedIntent.getStringExtra("website_key"); if (websiteName == null) { websiteName = "No website provided"; } // Display the received data if (websiteName != null) { displayTextView.setText("Website: " + websiteName); } // Check if intent has specific extra if (receivedIntent.hasExtra("website_key")) { String name = receivedIntent.getStringExtra("website_key"); Toast.makeText(this, "Received: " + name, Toast.LENGTH_SHORT).show(); } }}
SECTION B: (60 MARKS)
Attempt any THREE (3) out of FOUR (4) questions in this section.
Question Four
Many students now days fail to manage loan that they get from Higher Education Students Loan Board (HESLB). Most of them finish the money even before the period they are supposed to use. Based on the mentioned problem, HESLB wants a mobile application called Budget Planner that will helps their beneficiaries to manage their money. As a mobile apps developer, you have been consulted with HESLB to develop the Budget Planner mobile app that will do the following: receive the amount from the student (initial balance), the student will enter the number of days that he/she supposed to use the money. Every time the student spends a certain amount of money the app should deduct from the initial balance. Once the student is about to finishes the money, the app should alert him/her. The app should not allow the student to spend more than his/her initial balance.
Spending Prevention: Cannot spend more than available balance
Progress Visualization: Progress bar shows spending progress
Data Persistence: Saves budget data using SharedPreferences
Reset Functionality: Allows resetting the budget
User-friendly Interface: Clear warnings and alerts
Question Five
Currently, UDOM SACCOS have no tool to automate its operations as a result there is a delay in providing services to its customers. You have been asked by the management of UDOOM SACCOS to develop a mobile application that will store the information of its customers. The app should store name, college, age, gender and monthly contribution of its customers. The management want to show the reports of how customers are contributing monthly.
The management also wants to show the dates of different events within SACCOS such as the date of annual meeting etc. The management suggests the following features to be used: radio button for gender and spinner for college. Use progress bar to show the monthly contributions and calendar to show the dates of the events. Based on the given requirements, you found that the application has only two activities i.e. ActivityOne and ActivityTwo.
(20 Marks)
(a) Using the methods of Intent class show how you will send the customer’s data to the second activity.(5 Marks)
Answer (Click to show)
Sending customer data from ActivityOne to ActivityTwo using Intent:
In ActivityOne.java:
public class ActivityOne extends AppCompatActivity { // UI Elements private EditText etName, etAge, etContribution; private Spinner spinnerCollege; private RadioGroup radioGroupGender; private Button btnViewReport; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_one); initializeViews(); setupSpinner(); } private void initializeViews() { etName = findViewById(R.id.etName); etAge = findViewById(R.id.etAge); etContribution = findViewById(R.id.etContribution); spinnerCollege = findViewById(R.id.spinnerCollege); radioGroupGender = findViewById(R.id.radioGroupGender); btnViewReport = findViewById(R.id.btnViewReport); btnViewReport.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendCustomerDataToSecondActivity(); } }); } private void setupSpinner() { ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.colleges_array, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerCollege.setAdapter(adapter); } // Method to send customer data to ActivityTwo private void sendCustomerDataToSecondActivity() { // Validate input if (!validateInput()) { return; } // Collect customer data from input fields String name = etName.getText().toString().trim(); String college = spinnerCollege.getSelectedItem().toString(); int age = Integer.parseInt(etAge.getText().toString().trim()); // Get selected gender from RadioGroup int selectedGenderId = radioGroupGender.getCheckedRadioButtonId(); RadioButton selectedGenderButton = findViewById(selectedGenderId); String gender = selectedGenderButton.getText().toString(); double monthlyContribution = Double.parseDouble(etContribution.getText().toString().trim()); // Create Intent Intent intent = new Intent(ActivityOne.this, ActivityTwo.class); // Method 1: Using individual putExtra() methods intent.putExtra("customer_name", name); intent.putExtra("customer_college", college); intent.putExtra("customer_age", age); intent.putExtra("customer_gender", gender); intent.putExtra("customer_contribution", monthlyContribution); // Method 2: Using Bundle (alternative approach) Bundle customerBundle = new Bundle(); customerBundle.putString("name", name); customerBundle.putString("college", college); customerBundle.putInt("age", age); customerBundle.putString("gender", gender); customerBundle.putDouble("contribution", monthlyContribution); intent.putExtras(customerBundle); // Method 3: Using putExtras() with multiple values (for demonstration) intent.putExtra("customer_data", new String[]{name, college, gender}); intent.putExtra("customer_age", age); intent.putExtra("customer_contribution", monthlyContribution); // Add current date and time SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); String currentDateTime = sdf.format(new Date()); intent.putExtra("report_generated_time", currentDateTime); // Add flags if needed (optional) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Start the second activity startActivity(intent); } private boolean validateInput() { if (etName.getText().toString().trim().isEmpty()) { etName.setError("Name is required"); etName.requestFocus(); return false; } if (etAge.getText().toString().trim().isEmpty()) { etAge.setError("Age is required"); etAge.requestFocus(); return false; } if (etContribution.getText().toString().trim().isEmpty()) { etContribution.setError("Monthly contribution is required"); etContribution.requestFocus(); return false; } if (radioGroupGender.getCheckedRadioButtonId() == -1) { Toast.makeText(this, "Please select gender", Toast.LENGTH_SHORT).show(); return false; } return true; } // Alternative: Send multiple customers data (for batch reports) private void sendMultipleCustomersData(ArrayList<Customer> customersList) { Intent intent = new Intent(ActivityOne.this, ActivityTwo.class); // Convert ArrayList to Serializable intent.putExtra("customers_list", customersList); // Or convert to JSON string using Gson library // Gson gson = new Gson(); // String customersJson = gson.toJson(customersList); // intent.putExtra("customers_json", customersJson); startActivity(intent); } // Customer class for multiple data (Serializable for intent transfer) class Customer implements Serializable { String name, college, gender; int age; double contribution; public Customer(String name, String college, int age, String gender, double contribution) { this.name = name; this.college = college; this.age = age; this.gender = gender; this.contribution = contribution; } }}
(b) Using the methods of Intent class show how you will receive the customer’s data within the second activity.(5 Marks)
Answer (Click to show)
Receiving customer data in ActivityTwo:
In ActivityTwo.java:
public class ActivityTwo extends AppCompatActivity { private TextView tvName, tvCollege, tvAge, tvGender, tvContribution, tvReportTime; private ProgressBar progressBar; private CalendarView calendarView; private Button btnBack; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_two); initializeViews(); receiveCustomerData(); setupCalendar(); } private void initializeViews() { tvName = findViewById(R.id.tvName); tvCollege = findViewById(R.id.tvCollege); tvAge = findViewById(R.id.tvAge); tvGender = findViewById(R.id.tvGender); tvContribution = findViewById(R.id.tvContribution); tvReportTime = findViewById(R.id.tvReportTime); progressBar = findViewById(R.id.progressBar); calendarView = findViewById(R.id.calendarView); btnBack = findViewById(R.id.btnBack); btnBack.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); } private void receiveCustomerData() { // Get the intent that started this activity Intent receivedIntent = getIntent(); // Method 1: Using getStringExtra() and getIntExtra() etc. String name = receivedIntent.getStringExtra("customer_name"); String college = receivedIntent.getStringExtra("customer_college"); int age = receivedIntent.getIntExtra("customer_age", 0); String gender = receivedIntent.getStringExtra("customer_gender"); double contribution = receivedIntent.getDoubleExtra("customer_contribution", 0.0); String reportTime = receivedIntent.getStringExtra("report_generated_time"); // Method 2: Using Bundle Bundle bundle = receivedIntent.getExtras(); if (bundle != null) { name = bundle.getString("name", name); college = bundle.getString("college", college); age = bundle.getInt("age", age); gender = bundle.getString("gender", gender); contribution = bundle.getDouble("contribution", contribution); } // Method 3: With null checking and default values if (receivedIntent.hasExtra("customer_name")) { name = receivedIntent.getStringExtra("customer_name"); } else { name = "No name provided"; } // Method 4: Getting array data (alternative approach) String[] customerData = receivedIntent.getStringArrayExtra("customer_data"); if (customerData != null && customerData.length >= 3) { name = customerData[0]; college = customerData[1]; gender = customerData[2]; } // Display the received data displayCustomerData(name, college, age, gender, contribution, reportTime); // Show contribution on progress bar updateContributionProgress(contribution); // Show toast with received data summary Toast.makeText(this, "Data received for: " + name, Toast.LENGTH_SHORT).show(); } private void displayCustomerData(String name, String college, int age, String gender, double contribution, String reportTime) { tvName.setText("Name: " + (name != null ? name : "N/A")); tvCollege.setText("College: " + (college != null ? college : "N/A")); tvAge.setText("Age: " + age); tvGender.setText("Gender: " + (gender != null ? gender : "N/A")); tvContribution.setText("Monthly Contribution: TZS " + String.format("%,.2f", contribution)); if (reportTime != null && !reportTime.isEmpty()) { tvReportTime.setText("Report Generated: " + reportTime); } else { tvReportTime.setVisibility(View.GONE); } } private void updateContributionProgress(double contribution) { // Assuming maximum contribution is 500,000 TZS for progress bar scaling double maxContribution = 500000.0; int progress = (int) ((contribution / maxContribution) * 100); if (progress > 100) progress = 100; progressBar.setProgress(progress); // Set progress bar color based on contribution level if (progress < 30) { progressBar.setProgressTintList(ColorStateList.valueOf( getResources().getColor(android.R.color.holo_orange_dark))); } else if (progress < 70) { progressBar.setProgressTintList(ColorStateList.valueOf( getResources().getColor(android.R.color.holo_blue_dark))); } else { progressBar.setProgressTintList(ColorStateList.valueOf( getResources().getColor(android.R.color.holo_green_dark))); } } private void setupCalendar() { calendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() { @Override public void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth) { String selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year; checkForSACCOSEvent(selectedDate); } }); // Mark SACCOS events on calendar markSACCOSEvents(); } private void checkForSACCOSEvent(String date) { // Check if the selected date has an event HashMap<String, String> events = getSACCOSEvents(); if (events.containsKey(date)) { new AlertDialog.Builder(this) .setTitle("SACCOS EVENT") .setMessage("Event: " + events.get(date)) .setPositiveButton("OK", null) .setIcon(android.R.drawable.ic_dialog_info) .show(); } else { Toast.makeText(this, "No event scheduled for " + date, Toast.LENGTH_SHORT).show(); } } private HashMap<String, String> getSACCOSEvents() { HashMap<String, String> events = new HashMap<>(); events.put("15/3/2024", "Annual General Meeting"); events.put("1/4/2024", "Monthly Contribution Deadline"); events.put("20/4/2024", "Board Meeting"); events.put("1/5/2024", "Dividend Payout Day"); events.put("25/12/2024", "SACCOS Christmas Party"); events.put("30/6/2024", "Financial Year End Meeting"); events.put("15/1/2024", "New Members Orientation"); return events; } private void markSACCOSEvents() { // This would require custom calendar implementation // to highlight dates with events. For demonstration, // we're using a simple Toast notification when dates are selected. } // Method to receive multiple customers (if sent as list) private void receiveMultipleCustomers(Intent intent) { ArrayList<Customer> customersList = (ArrayList<Customer>) intent.getSerializableExtra("customers_list"); if (customersList != null) { StringBuilder report = new StringBuilder(); double totalContributions = 0; for (Customer customer : customersList) { report.append(customer.name).append(": TZS ") .append(customer.contribution).append("\n"); totalContributions += customer.contribution; } report.append("\nTotal Contributions: TZS ").append(totalContributions); report.append("\nAverage: TZS ").append(totalContributions / customersList.size()); // Display in a TextView or AlertDialog new AlertDialog.Builder(this) .setTitle("Multiple Customers Report") .setMessage(report.toString()) .setPositiveButton("OK", null) .show(); } } // Customer class (must match the one in ActivityOne) class Customer implements Serializable { String name, college, gender; int age; double contribution; }}
(c) By the help of SQLiteOpenHelper class, show how you can save the customer’s details in a database. Use user and customer as the name of the database and table respectively.(10 Marks)
Answer (Click to show)
Complete SQLiteOpenHelper Implementation for Customer Database:
1. CustomerContract.java - Contract Class
import android.provider.BaseColumns;public class CustomerContract { private CustomerContract() {} public static class CustomerEntry implements BaseColumns { public static final String TABLE_NAME = "customer"; public static final String COLUMN_NAME = "name"; public static final String COLUMN_COLLEGE = "college"; public static final String COLUMN_AGE = "age"; public static final String COLUMN_GENDER = "gender"; public static final String COLUMN_CONTRIBUTION = "contribution"; public static final String COLUMN_REGISTRATION_DATE = "registration_date"; public static final String COLUMN_IS_ACTIVE = "is_active"; }}
The prime purpose of a content provider is to serve as a central repository of data where users can store and can fetch the data. The access of this repository is given to other applications also but in a safe manner in order to serve the different requirements of the user. Write an application that will allow users store their details such as name and phone numbers in a content provider. The application should also allow other applications to retrieve the stored user’s information. The application has one activity called MainActivity, two edit texts for name and phone number and two buttons for save data and view data. The ids for the views are etName, etPhoneNumber, btnSave and btnView.
import android.net.Uri;import android.provider.BaseColumns;public class UserContract { // Authority - must match provider authority in AndroidManifest.xml public static final String AUTHORITY = "com.example.userprovider"; // Base content URI public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY); // Path for users table public static final String PATH_USERS = "users"; // Inner class defining table contents public static class UserEntry implements BaseColumns { // Content URI for the users table public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_USERS); // Table name public static final String TABLE_NAME = "users"; // Column names public static final String COLUMN_NAME = "name"; public static final String COLUMN_PHONE = "phone"; public static final String COLUMN_CREATED_AT = "created_at"; // MIME types public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd." + AUTHORITY + "." + PATH_USERS; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd." + AUTHORITY + "." + PATH_USERS; }}
2. UserDbHelper.java - Database Helper
import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class UserDbHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "user.db"; private static final int DATABASE_VERSION = 1; // SQL statement to create the users table private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + UserContract.UserEntry.TABLE_NAME + " (" + UserContract.UserEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + UserContract.UserEntry.COLUMN_NAME + " TEXT NOT NULL, " + UserContract.UserEntry.COLUMN_PHONE + " TEXT NOT NULL, " + UserContract.UserEntry.COLUMN_CREATED_AT + " TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"; // SQL statement to drop the users table private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + UserContract.UserEntry.TABLE_NAME; public UserDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); }}
7. Example of Another App Accessing the Content Provider
// This could be in a separate application that accesses the content providerpublic class OtherAppActivity extends AppCompatActivity { private void accessUserDataFromProvider() { // Content provider URI from the first app Uri uri = Uri.parse("content://com.example.userprovider/users"); // Query data Cursor cursor = getContentResolver().query( uri, null, null, null, null ); if (cursor != null && cursor.moveToFirst()) { StringBuilder data = new StringBuilder(); do { String name = cursor.getString(cursor.getColumnIndex("name")); String phone = cursor.getString(cursor.getColumnIndex("phone")); data.append(name).append(": ").append(phone).append("\n"); } while (cursor.moveToNext()); cursor.close(); // Display the data from other app's content provider Toast.makeText(this, "Data from provider:\n" + data.toString(), Toast.LENGTH_LONG).show(); } }}
Question Seven
Android Fragment represents a behavior or a portion of a user interface in an activity. Multiple fragments can be combined in a single activity to build a multi- panel User Interface (UI) and reuse a fragment in multiple activities. As a mobile apps developer, you have been asked to develop an application with two fragments named FragmentOne and FragmentTwo. When a user click on the specific fragment, the description of the fragment displays on the fragment container in the main activity which hosts the fragments. Assume the ids for FragmentOne and FragmentTwo are btnFragment_one and btnFragment_two respectively. Hint: use linear layout to create a fragment container.
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="20dp" android:background="#F3E5F5"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="16dp"> <ImageView android:layout_width="48dp" android:layout_height="48dp" android:src="@android:drawable/ic_dialog_info" android:contentDescription="Info Icon" android:layout_marginEnd="12dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="FRAGMENT ONE" android:textSize="28sp" android:textColor="#6200EE" android:textStyle="bold"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="2dp" android:background="#6200EE" android:layout_marginBottom="16dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is Fragment One description." android:textSize="20sp" android:textColor="#333333" android:textStyle="bold" android:layout_marginBottom="12dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Fragment One represents the first portion of the user interface. It contains specific functionality and UI elements that are part of the main activity. Clicking on Fragment One button displays this description in the fragment container." android:textSize="16sp" android:textColor="#555555" android:lineSpacingExtra="4dp" android:padding="16dp" android:background="#FFFFFF" android:elevation="2dp" android:layout_marginBottom="16dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:layout_marginTop="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Features:" android:textSize="16sp" android:textStyle="bold" android:textColor="#6200EE" android:layout_marginEnd="8dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UI Component, Reusable, Modular" android:textSize="14sp" android:textColor="#757575"/> </LinearLayout></LinearLayout>
3. fragment_two.xml - Layout for FragmentTwo
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="20dp" android:background="#E0F2F1"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="16dp"> <ImageView android:layout_width="48dp" android:layout_height="48dp" android:src="@android:drawable/ic_dialog_map" android:contentDescription="Map Icon" android:layout_marginEnd="12dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="FRAGMENT TWO" android:textSize="28sp" android:textColor="#03DAC5" android:textStyle="bold"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="2dp" android:background="#03DAC5" android:layout_marginBottom="16dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is Fragment Two description." android:textSize="20sp" android:textColor="#333333" android:textStyle="bold" android:layout_marginBottom="12dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Fragment Two represents the second portion of the user interface. It contains different functionality and UI elements. Multiple fragments can be combined in a single activity to build multi-panel UIs and fragments can be reused in multiple activities." android:textSize="16sp" android:textColor="#555555" android:lineSpacingExtra="4dp" android:padding="16dp" android:background="#FFFFFF" android:elevation="2dp" android:layout_marginBottom="16dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Benefits of Fragments:" android:textSize="16sp" android:textStyle="bold" android:textColor="#03DAC5" android:layout_marginTop="8dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="âś“ Modularity\nâś“ Reusability\nâś“ Multi-panel layouts\nâś“ Back stack management\nâś“ Adaptability to different screen sizes" android:textSize="14sp" android:textColor="#555555" android:lineSpacingExtra="4dp" android:padding="12dp" android:background="#FFFFFF" android:elevation="1dp"/></LinearLayout>
4. FragmentOne.java - First Fragment Class
import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import androidx.fragment.app.Fragment;public class FragmentOne extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_one, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Additional initialization after view is created // You can set click listeners, adapters, etc. here } // Method to update fragment content with arguments public static FragmentOne newInstance(String param) { FragmentOne fragment = new FragmentOne(); Bundle args = new Bundle(); args.putString("param", param); fragment.setArguments(args); return fragment; }}
5. FragmentTwo.java - Second Fragment Class
import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import androidx.fragment.app.Fragment;public class FragmentTwo extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_two, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Additional initialization after view is created // You can set click listeners, adapters, etc. here } // Method to update fragment content with arguments public static FragmentTwo newInstance(String param) { FragmentTwo fragment = new FragmentTwo(); Bundle args = new Bundle(); args.putString("param", param); fragment.setArguments(args); return fragment; }}
6. MainActivity.java - Host Activity
import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import androidx.fragment.app.Fragment;import androidx.fragment.app.FragmentManager;import androidx.fragment.app.FragmentTransaction;public class MainActivity extends AppCompatActivity { private Button btnFragmentOne, btnFragmentTwo; private TextView tvStatus; private FragmentManager fragmentManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initializeViews(); setupFragmentManager(); setupClickListeners(); // Load default fragment (optional) if (savedInstanceState == null) { loadFragment(new FragmentOne(), "Fragment One"); } } private void initializeViews() { btnFragmentOne = findViewById(R.id.btnFragment_one); btnFragmentTwo = findViewById(R.id.btnFragment_two); tvStatus = findViewById(R.id.tvStatus); } private void setupFragmentManager() { fragmentManager = getSupportFragmentManager(); // Add back stack change listener fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { updateStatusFromBackStack(); } }); } private void setupClickListeners() { btnFragmentOne.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { loadFragment(new FragmentOne(), "Fragment One"); showToast("Loading Fragment One"); } }); btnFragmentTwo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { loadFragment(new FragmentTwo(), "Fragment Two"); showToast("Loading Fragment Two"); } }); } /** * Method to load fragment into container */ private void loadFragment(Fragment fragment, String fragmentName) { // Begin fragment transaction FragmentTransaction transaction = fragmentManager.beginTransaction(); // Add custom animations transaction.setCustomAnimations( android.R.anim.fade_in, // enter android.R.anim.fade_out, // exit android.R.anim.slide_in_left, // popEnter android.R.anim.slide_out_right // popExit ); // Replace the fragment in the container transaction.replace(R.id.fragment_container, fragment); // Add to back stack (allows back navigation) transaction.addToBackStack(fragmentName); // Commit the transaction transaction.commit(); // Update status tvStatus.setText("Currently viewing: " + fragmentName); } /** * Update status based on back stack */ private void updateStatusFromBackStack() { int backStackCount = fragmentManager.getBackStackEntryCount(); if (backStackCount > 0) { FragmentManager.BackStackEntry entry = fragmentManager.getBackStackEntryAt(backStackCount - 1); tvStatus.setText("Currently viewing: " + entry.getName()); } else { tvStatus.setText("No fragment selected"); } } /** * Method to load fragment with data */ private void loadFragmentWithData(Fragment fragment, String fragmentName, Bundle data) { fragment.setArguments(data); loadFragment(fragment, fragmentName); } /** * Method to clear back stack */ private void clearBackStack() { fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); tvStatus.setText("Back stack cleared"); } /** * Show toast message */ private void showToast(String message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } /** * Handle back button to navigate through fragments */ @Override public void onBackPressed() { if (fragmentManager.getBackStackEntryCount() > 1) { fragmentManager.popBackStack(); } else { super.onBackPressed(); } } /** * Method to get current fragment */ private Fragment getCurrentFragment() { return fragmentManager.findFragmentById(R.id.fragment_container); } /** * Method to communicate between fragments (example) */ private void communicateBetweenFragments(String message) { Fragment currentFragment = getCurrentFragment(); if (currentFragment instanceof FragmentOne) { // Send message to FragmentOne Bundle result = new Bundle(); result.putString("message", message); getSupportFragmentManager().setFragmentResult("requestKey", result); } }}
7. Alternative Approach with Interface Communication (Bonus)
// Interface for fragment communicationpublic interface OnFragmentInteractionListener { void onFragmentInteraction(String fragmentName, String message);}// Modified FragmentOne with interfacepublic class FragmentOne extends Fragment { private OnFragmentInteractionListener listener; @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnFragmentInteractionListener) { listener = (OnFragmentInteractionListener) context; } } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Example button in fragment Button btnAction = view.findViewById(R.id.btnFragmentAction); btnAction.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (listener != null) { listener.onFragmentInteraction("FragmentOne", "Button clicked in Fragment One"); } } }); } @Override public void onDetach() { super.onDetach(); listener = null; }}// MainActivity implementing the interfacepublic class MainActivity extends AppCompatActivity implements OnFragmentInteractionListener { @Override public void onFragmentInteraction(String fragmentName, String message) { // Handle communication from fragments Toast.makeText(this, fragmentName + ": " + message, Toast.LENGTH_SHORT).show(); tvStatus.setText("Interaction: " + message); }}
Key Features Implemented:
Two Fragments: FragmentOne and FragmentTwo with distinct layouts
Fragment Container: FrameLayout in LinearLayout to host fragments
Fragment Transactions: Using FragmentManager to replace fragments
Click Handling: Buttons trigger fragment loading
Animations: Custom fade and slide animations for transitions
Back Stack Management: Allows back navigation between fragments
Status Display: Shows which fragment is currently displayed
Communication Options: Interface-based and Bundle-based communication
Toast Notifications: User feedback on actions
Reusable Design: Fragments can be reused in other activities