Compare commits

..

No commits in common. "9bbda7bf99913cfb7892d6a036cfb1c1c63c7157" and "3b34db75a5a3168e1025392b49e2998e3045ce3b" have entirely different histories.

12 changed files with 122 additions and 250 deletions

View File

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ThrowablePrintStackTrace" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@ -29,30 +29,21 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
packagingOptions {
exclude("META-INF/DEPENDENCIES")
exclude("META-INF/LICENSE")
exclude("META-INF/LICENSE.txt")
exclude("META-INF/license.txt")
exclude("META-INF/NOTICE")
exclude("META-INF/NOTICE.txt")
exclude("META-INF/notice.txt")
}
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
implementation(libs.work.runtime)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
}

View File

@ -2,10 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"

View File

@ -59,4 +59,10 @@ public class DatabaseHelper extends SQLiteOpenHelper {
cursor.close();
return tasks; //Zwrócenie listy zadań
}
public void removeExpiredTasks() {
SQLiteDatabase db = this.getWritableDatabase();
String currentDate = new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date());
db.delete("tasks", "deadline < ?", new String[]{currentDate}); //Usunięcie zadań z przeszłą datą
}
}

View File

@ -1,10 +1,10 @@
package com.example.todoapp2;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
@ -15,145 +15,118 @@ import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
private ArrayAdapter<String> itemsAdapter;//Adapter do zarządzania listą zadań
private EditText deadlineInput;//Pole wejściowe dla daty i godziny deadline'u
private Spinner priorityInput;//Spinner do wyboru priorytetu zadania
private TaskViewModel viewModel;//ViewModel do zarządzania danymi zadań
private static final String TAG = "MainActivity";
private ArrayAdapter<String> itemsAdapter;
private EditText deadlineInput;
private Spinner priorityInput;
private TaskViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);//Włączenie trybu Edge-to-Edge dla lepszego UX
setContentView(R.layout.activity_main);//Ustawienie widoku aktywności
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
viewModel = new ViewModelProvider(this).get(TaskViewModel.class); //Inicjalizacja ViewModel
ListView list = findViewById(R.id.list);//Inicjalizacja listy zadań
Button button = findViewById(R.id.button);//Inicjalizacja przycisku dodawania zadań
Button buttonViewHtml = findViewById(R.id.button_view_html);//Inicjalizacja przycisku widoku HTML
deadlineInput = findViewById(R.id.edit_deadline);//Inicjalizacja pola wejściowego deadline'u
priorityInput = findViewById(R.id.edit_priority);//Inicjalizacja spinnera priorytetu
ListView list = findViewById(R.id.list); //Referencja do ListView
Button button = findViewById(R.id.button); //Referencja do przycisku dodawania zadań
Button buttonViewHtml = findViewById(R.id.button_view_html); //Referencja do przycisku wyświetlania zadań w HTML
deadlineInput = findViewById(R.id.edit_deadline); //Referencja do pola wyboru terminu
priorityInput = findViewById(R.id.edit_priority); //Referencja do spinnera wyboru priorytetu
itemsAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);//Konfiguracja adaptera dla listy zadań
list.setAdapter(itemsAdapter);//Ustawienie adaptera dla listy
itemsAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1); //Inicjalizacja adaptera dla ListView
list.setAdapter(itemsAdapter); //Ustawienie adaptera na ListView
viewModel.getTasks().observe(this, tasks -> {//Obserwacja zmian w liście zadań
itemsAdapter.clear();//Wyczyszczenie adaptera
itemsAdapter.addAll(tasks);//Dodanie nowych zadań do adaptera
//Obserwacja zmian w LiveData
viewModel.getTasks().observe(this, tasks -> {
Log.d(TAG, "Tasks updated: " + tasks); //Logowanie zmian
itemsAdapter.clear(); //Czyszczenie adaptera
itemsAdapter.addAll(tasks); //Dodanie nowych danych do adaptera
itemsAdapter.notifyDataSetChanged(); //Powiadomienie adaptera o zmianach
});
deadlineInput.setOnClickListener(v -> showDatePickerDialog());//Ustawienie kliknięcia w pole deadline, aby wyświetlić DatePickerDialog
button.setOnClickListener(view -> addItem());//Ustawienie kliknięcia w przycisk dodawania zadania
buttonViewHtml.setOnClickListener(v -> viewTasksInHtml());//Ustawienie kliknięcia w przycisk widoku HTML
list.setOnItemLongClickListener((parent, view, position, id) -> {//Ustawienie długiego kliknięcia na element listy, aby usunąć zadanie
remove(position);
return true;
//Listener dla pola wyboru terminu
deadlineInput.setOnClickListener(v -> {
showDatePickerDialog(); //Wyświetlenie DatePickerDialog
});
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,//Ustawienie adaptera dla priorytetów
//Listener dla przycisku dodawania zadań
button.setOnClickListener(view -> {
Log.d(TAG, "Add button clicked"); //Logowanie kliknięcia przycisku
addItem(); //Dodanie nowego zadania
});
//Listener dla przycisku wyświetlania zadań w HTML
buttonViewHtml.setOnClickListener(v -> {
viewTasksInHtml(); //Wyświetlenie zadań w HTML
});
//Listener dla długiego kliknięcia na element ListView
list.setOnItemLongClickListener((parent, view, position, id) -> {
return remove(position); //Usunięcie zadania
});
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, //Inicjalizacja adaptera dla spinnera
R.array.priority_levels, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
priorityInput.setAdapter(adapter);
priorityInput.setAdapter(adapter); //Ustawienie adaptera na spinner
}
private void showDatePickerDialog() {
private void showDatePickerDialog() { //Metoda wyświetlająca DatePickerDialog
final Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
DatePickerDialog datePickerDialog = new DatePickerDialog(this, (view, year1, month1, dayOfMonth) -> {//Tworzenie DatePickerDialog do wyboru daty
TimePickerDialog timePickerDialog = new TimePickerDialog(this, (timeView, hourOfDay, minuteOfHour) -> {//Tworzenie TimePickerDialog do wyboru godziny po wybraniu daty
String date = year1 + "-" + (month1 + 1) + "-" + dayOfMonth + " " + hourOfDay + ":" + minuteOfHour + ":00";
deadlineInput.setText(date);//Ustawienie wybranej daty i godziny w polu deadline
}, hour, minute, true);
timePickerDialog.show();
//Listener dla wyboru daty
DatePickerDialog datePickerDialog = new DatePickerDialog(this, (view, year1, month1, dayOfMonth) -> {
String date = year1 + "-" + (month1 + 1) + "-" + dayOfMonth;
deadlineInput.setText(date); //Ustawienie wybranej daty w polu tekstowym
}, year, month, day);
datePickerDialog.show();
datePickerDialog.show(); //Wyświetlenie DatePickerDialog
}
private void remove(int position) {
private boolean remove(int position) { //Metoda usuwająca zadanie
Context context = getApplicationContext();
String item = itemsAdapter.getItem(position);
String item = itemsAdapter.getItem(position); //Pobranie elementu z adaptera
assert item != null;
String task = item.split(" \\(Deadline:")[0];
if (viewModel.removeTask(task)) { //Usunięcie zadania z ViewModel
Toast.makeText(context, "Item removed", Toast.LENGTH_LONG).show();//Wyświetlenie komunikatu o usunięciu zadania
Toast.makeText(context, "Item removed", Toast.LENGTH_LONG).show(); //Wyświetlenie Toast z komunikatem
return true;
} else {
Toast.makeText(context, "Error removing item", Toast.LENGTH_LONG).show();//Wyświetlenie komunikatu o błędzie usuwania zadania
Toast.makeText(context, "Error removing item", Toast.LENGTH_LONG).show(); //Wyświetlenie Toast z błędem
return false;
}
}
private void addItem() {
private void addItem() { //Metoda dodająca nowe zadanie
EditText taskInput = findViewById(R.id.edit_text);
String taskText = taskInput.getText().toString();
String deadlineText = deadlineInput.getText().toString();
String priorityText = priorityInput.getSelectedItem().toString();
if (!(taskText.isEmpty() || deadlineText.isEmpty() || priorityText.isEmpty())) {
Log.d(TAG, "Task: " + taskText + ", Deadline: " + deadlineText + ", Priority: " + priorityText);
if (!(taskText.isEmpty() || deadlineText.isEmpty() || priorityText.isEmpty())) { //Sprawdzenie czy pola nie puste
if (viewModel.addTask(taskText, deadlineText, priorityText)) { //Dodanie zadania do ViewModel
taskInput.setText("");//Wyczyszczenie pola tekstowego zadania
deadlineInput.setText("");//Wyczyszczenie pola tekstowego deadline'u
priorityInput.setSelection(0);//Resetowanie wyboru priorytetu
sendSMSReminder(taskText, deadlineText);//Wysłanie przypomnienia SMS o nowym zadaniu
scheduleDeadlineReminder(taskText, deadlineText);//Zaprogramowanie przypomnienia SMS na 5 minut przed deadlinem
taskInput.setText(""); //Wyczyszczenie pola tekstowego
deadlineInput.setText(""); //Wyczyszczenie pola daty
priorityInput.setSelection(0); //Resetowanie spinnera
} else {
Toast.makeText(getApplicationContext(), "Error adding task", Toast.LENGTH_LONG).show();//Wyświetlenie komunikatu o błędzie dodawania zadania
Toast.makeText(getApplicationContext(), "Error adding task", Toast.LENGTH_LONG).show(); //Wyświetlenie Toast z błędem
}
} else {
Toast.makeText(getApplicationContext(), "Please enter all fields", Toast.LENGTH_LONG).show();//Wyświetlenie komunikatu o konieczności wypełnienia wszystkich pól
Toast.makeText(getApplicationContext(), "Please enter all fields", Toast.LENGTH_LONG).show(); //Wyświetlenie Toast z komunikatem o wypełnieniu wszystkich pól
}
}
private void sendSMSReminder(String task, String deadline) {
String phoneNumber = "502836547";//Zamień na numer telefonu, na który ma być wysłany SMS
String message = "New task added: " + task + ", Deadline: " + deadline;
SmsApiClient.sendSms(phoneNumber, message);//Wysłanie SMS za pomocą SmsApiClient
}
private void scheduleDeadlineReminder(String task, String deadline) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date deadlineDate = dateFormat.parse(deadline);
if (deadlineDate != null) {
long timeDiff = deadlineDate.getTime() - System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5);//5 minut przed deadline
if (timeDiff > 0) {
Data data = new Data.Builder()
.putString("task", task)
.putString("phoneNumber", "502836547")//Zamień na numer telefonu
.build();
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(ReminderWorker.class)
.setInitialDelay(timeDiff, TimeUnit.MILLISECONDS)//Ustawienie opóźnienia na 5 minut przed deadline
.setInputData(data)//Przekazanie danych do ReminderWorker
.build();
WorkManager.getInstance(this).enqueue(workRequest);//Dodanie zadania do WorkManager
}
}
} catch (ParseException e) {
e.printStackTrace();
}
}
private void viewTasksInHtml() {
private void viewTasksInHtml() { //Metoda wyświetlająca zadania w HTML
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.append("<html><body><h1>Task List</h1><table border='1'><tr><th>Task</th><th>Deadline</th><th>Priority</th></tr>");
for (int i = 0; i < itemsAdapter.getCount(); i++) {
@ -167,9 +140,8 @@ public class MainActivity extends AppCompatActivity {
}
htmlBuilder.append("</table></body></html>");
//Przekazanie danych HTML do HtmlActivity za pomocą Intent
Intent intent = new Intent(this, HtmlActivity.class);
intent.putExtra("html_data", htmlBuilder.toString());
startActivity(intent);
Intent intent = new Intent(MainActivity.this, HtmlActivity.class);
intent.putExtra("html_data", htmlBuilder.toString()); //Przekazanie danych HTML do nowej aktywności
startActivity(intent); //Rozpoczęcie nowej aktywności
}
}

View File

@ -1,29 +0,0 @@
package com.example.todoapp2;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class ReminderWorker extends Worker {
public ReminderWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
//Pobranie danych przekazanych do Workera
String task = getInputData().getString("task");
String phoneNumber = getInputData().getString("phoneNumber");
//Wysłanie przypomnienia SMS
if (task != null && phoneNumber != null) {
SmsApiClient.sendSms(phoneNumber, "Deadline zadania \"" + task + "\" jest dzisiaj!");
}
return Result.success(); //Zwrócenie sukcesu po wykonaniu pracy
}
}

View File

@ -1,58 +0,0 @@
package com.example.todoapp2;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.Header;
import retrofit2.http.POST;
public class SmsApiClient {
private static final String BASE_URL = "https://api.smsapi.pl/";
private static final String TOKEN = "Bearer s1lgvSn5RdZNATyFq1LcPhGlqC13oeTU9fcXd5Ar";
public interface SmsApiService {
@FormUrlEncoded
@POST("sms.do")
Call<Void> sendSms(
@Header("Authorization") String authorization,
@Field("to") String to,
@Field("message") String message,
@Field("format") String format
);
}
private static SmsApiService smsApiService;
//Singleton pattern do uzyskania instancji SmsApiService
public static SmsApiService getInstance() {
if (smsApiService == null) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
smsApiService = retrofit.create(SmsApiService.class);
}
return smsApiService;
}
//Metoda do wysyłania SMS za pomocą SmsApiService
public static void sendSms(String to, String message) {
SmsApiService service = getInstance();
service.sendSms(TOKEN, to, message, "json")
.enqueue(new retrofit2.Callback<Void>() {
@Override
public void onResponse(@NonNull Call<Void> call, @NonNull retrofit2.Response<Void> response) {
//SMS sent successfully
}
@Override
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
//Failed to send SMS
}
});
}
}

View File

@ -16,6 +16,7 @@ public class TaskViewModel extends AndroidViewModel {
super(application);
db = new DatabaseHelper(application); //Inicjalizacja bazy danych
tasks = new MutableLiveData<>();
removeExpiredTasks(); //Usuwanie przeterminowanych zadań
loadTasks(); //Załadowanie zadań z bazy danych
}
@ -30,6 +31,7 @@ public class TaskViewModel extends AndroidViewModel {
public boolean addTask(String task, String deadline, String priority) {
boolean result = db.addTask(task, deadline, priority); //Dodanie zadania do bazy danych
if (result) {
removeExpiredTasks(); //Usuwanie przeterminowanych zadań
loadTasks(); //Załadowanie zadań po dodaniu nowego
}
return result;
@ -38,8 +40,13 @@ public class TaskViewModel extends AndroidViewModel {
public boolean removeTask(String task) {
boolean result = db.removeTask(task); //Usunięcie zadania z bazy danych
if (result) {
removeExpiredTasks(); //Usuwanie przeterminowanych zadań
loadTasks(); //Załadowanie zadań po usunięciu
}
return result;
}
private void removeExpiredTasks() {
db.removeExpiredTasks(); //Usunięcie przeterminowanych zadań z bazy danych
}
}

View File

@ -67,21 +67,21 @@
android:hint="Enter Task"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_view_html"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="24dp"
android:text="View Tasks in HTML"
tools:ignore="HardcodedText" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:text="ADD"
android:layout_marginBottom="24dp"
tools:ignore="HardcodedText" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_view_html"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View Tasks in HTML"
android:layout_gravity="center"
android:layout_marginBottom="24dp"
tools:ignore="HardcodedText" />
<com.google.android.material.card.MaterialCardView

View File

@ -1,5 +0,0 @@
<resources>
<string name="twilio_account_sid">AC55868eabe6daa1aabfe8805d8e04dc5b</string>
<string name="twilio_auth_token">7d117e67f36da055f2f424645ae8bdcd</string>
<string name="twilio_phone_number">+17752640737</string>
</resources>

View File

@ -7,7 +7,6 @@ appcompat = "1.7.0"
material = "1.12.0"
activity = "1.9.0"
constraintlayout = "2.1.4"
workRuntime = "2.9.0"
[libraries]
@ -18,7 +17,6 @@ appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "a
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
work-runtime = { group = "androidx.work", name = "work-runtime", version.ref = "workRuntime" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }