Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CMPUT 301 Student Submission License
Version 2.0

Copyright 2025 `<student name>`
Copyright 2025 `xindi li`

Unauthorized redistribution is forbidden under all circumstances. Use of this
software without explicit authorization from the author **and** the CMPUT 301
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

## References and Resources

List any resources used here, or simply put `N/A` if not applicable.

N/A

## Verbal Collaboration

| Student Name | CCID |
| ------------ | --------- |
| `student` | `student` |
| `Xindi Li` | `xindi8` |
| `<Add more>` | `<CCID>` |
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.example.listycitylab3;

import static java.security.AccessController.getContext;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;


public class AddCityFragment extends DialogFragment {
interface AddCityDialogListener {
void addCity(City city);
}
private AddCityDialogListener listener;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof AddCityDialogListener) {
listener = (AddCityDialogListener) context;
} else {
throw new RuntimeException(context + "must implement AddCityDialogListener");
}
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
View view =
LayoutInflater.from(getContext()).inflate(R.layout.fragment_add_city, null);
EditText editCityName = view.findViewById(R.id.add_text_city_text);
EditText editProvinceName = view.findViewById(R.id.add_text_province_text);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
return builder
.setView(view)
.setTitle("Add a city")
.setNegativeButton("Cancel", null)
.setPositiveButton("Add", (dialog, which) -> {
String cityName = editCityName.getText().toString();
String provinceName = editProvinceName.getText().toString();
listener.addCity(new City(cityName, provinceName));
})
.create();
}
}

26 changes: 26 additions & 0 deletions code/app/src/main/java/com/example/listycitylab3/City.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.listycitylab3;

import java.io.Serializable;

public class City implements Serializable {
private String name;
private String province;
public City(String name, String province) {
this.name = name;
this.province = province;
}
public String getName() {
return name;
}
public String getProvince() {
return province;
}

public void setName(String name) {
this.name = name;
}

public void setProvince(String province) {
this.province = province;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.listycitylab3;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.ArrayList;

public class CityArrayAdapter extends ArrayAdapter<City> {
public CityArrayAdapter(Context context, ArrayList<City> cities) {
super(context, 0, cities);
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup
parent) {
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(R.layout.content,
parent, false);
} else {
view = convertView;
}
City city = getItem(position);
TextView cityName = view.findViewById(R.id.city_text);
TextView provinceName = view.findViewById(R.id.province_text);

cityName.setText(city.getName());
provinceName.setText(city.getProvince());
return view;
}
}
92 changes: 92 additions & 0 deletions code/app/src/main/java/com/example/listycitylab3/EditFragment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.example.listycitylab3;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;



// A DialogFragment that lets the user edit an existing City object
public class EditFragment extends DialogFragment {

// Callback interface so the host Activity can receive the edited City
public interface EditCityDialogListener {
void onCityEdited(int position, City updatedCity);
}

private EditCityDialogListener listener; // reference to the host Activity

// Factory method to create a new instance of the EditFragment
// Takes a City object and its position in the list, puts them in a Bundle
public static EditFragment newInstance(City city, int position) {
Bundle args = new Bundle();
args.putSerializable("city", city); // City must implement Serializable
args.putInt("pos", position); // keep track of which item is being edited
EditFragment f = new EditFragment();
f.setArguments(args); // attach arguments to the fragment
return f;
}

// Called when the fragment is attached to its host Activity
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// Ensure the host Activity implements the callback interface
if (context instanceof EditCityDialogListener) {
listener = (EditCityDialogListener) context;
} else {
throw new RuntimeException(context + " must implement EditCityDialogListener");
}
}

// Create and return the actual dialog UI
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
// Retrieve the City object and its position from arguments
City city = (City) requireArguments().getSerializable("city");
int position = requireArguments().getInt("pos");

// Inflate the custom layout for editing a city
View view = LayoutInflater.from(requireContext())
.inflate(R.layout.fragment_edit_city, null);

// Get references to the EditText fields
EditText editCityName = view.findViewById(R.id.edit_text_city_text);
EditText editProvinceName = view.findViewById(R.id.edit_text_province_text);

// Pre-fill the text fields with the current city data (if provided)
if (city != null) {
editCityName.setText(city.getName());
editProvinceName.setText(city.getProvince());
}

// Build and return the AlertDialog
return new AlertDialog.Builder(requireContext())
.setTitle("Edit City") // dialog title
.setView(view) // set the custom layout
.setPositiveButton("Confirm", (d, w) -> {
// When user clicks Confirm, grab the new values
String newName = editCityName.getText().toString().trim();
String newProv = editProvinceName.getText().toString().trim();

// Create a new City object with updated values
// (or you could directly mutate the existing City via setters)
City updated = new City(newName, newProv);

// Notify the Activity through the callback
listener.onCityEdited(position, updated);
})
.setNegativeButton("Cancel", null) // Dismiss if Cancel pressed
.create();
}
}

61 changes: 47 additions & 14 deletions code/app/src/main/java/com/example/listycitylab3/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,69 @@
package com.example.listycitylab3;

import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import androidx.appcompat.app.AppCompatActivity;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import java.util.ArrayList;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
public class MainActivity extends AppCompatActivity
implements AddCityFragment.AddCityDialogListener,
EditFragment.EditCityDialogListener {

private ArrayList<String> dataList;
private ArrayList<City> dataList;
private ListView cityList;
private ArrayAdapter<String> cityAdapter;
private CityArrayAdapter cityAdapter;

// AddCity callback
@Override
public void addCity(City city) {
cityAdapter.add(city);
cityAdapter.notifyDataSetChanged();
}

// EditCity callback
@Override
public void onCityEdited(int position, City updatedCity) {
// Replace the item at the exact position and refresh
dataList.set(position, updatedCity);
cityAdapter.notifyDataSetChanged();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

String[] cities = {
"Edmonton", "Vancouver", "Moscow",
"Sydney", "Berlin", "Vienna",
"Tokyo", "Beijing", "Osaka", "New Delhi"
};
// Seed data
String[] cities = {"Edmonton", "Vancouver", "Toronto"};
String[] provinces = {"AB", "BC", "ON"};

dataList = new ArrayList<>();
dataList.addAll(Arrays.asList(cities));

for (int i = 0; i < cities.length; i++) {
dataList.add(new City(cities[i], provinces[i]));
}

// List + adapter
cityList = findViewById(R.id.city_list);
cityAdapter = new ArrayAdapter<>(this, R.layout.content, dataList);

cityAdapter = new CityArrayAdapter(this, dataList);
cityList.setAdapter(cityAdapter);

// Add City
FloatingActionButton fab = findViewById(R.id.button_add_city);
fab.setOnClickListener(v ->
new AddCityFragment().show(getSupportFragmentManager(), "Add City")
);

// Edit City
cityList.setOnItemClickListener((parent, view, position, id) -> {
City selected = dataList.get(position);
EditFragment.newInstance(selected, position)
.show(getSupportFragmentManager(), "Edit City");
});
}
}
}

33 changes: 17 additions & 16 deletions code/app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textViewTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cities"
android:textSize="20sp"
android:textStyle="bold"
android:layout_margin="16dp"/>

<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cities"
android:textAppearance="?attr/textAppearanceHeadline6" />

</com.google.android.material.appbar.MaterialToolbar>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/button_add_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
app:srcCompat="@android:drawable/ic_input_add" />

<ListView
android:id="@+id/city_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="false"
android:padding="8dp" />
android:id="@+id/city_list" />

</LinearLayout>

Loading