diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 070cae303cb9618219d8f48249cb50e33b16f3d9..41368bcf9588df435f77fe288f4533ed59ccd675 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,8 @@ android:maxSdkVersion="18" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> + <application android:allowBackup="false" android:icon="@mipmap/ic_launcher" @@ -47,6 +49,34 @@ </intent-filter> </service> + <service + android:name=".services.notification.NotificationReadService" + android:exported="false" + android:permission="android.permission.BIND_JOB_SERVICE"> + <intent-filter> + <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" /> + </intent-filter> + </service> + + <service + android:name=".services.notification.NotificationsListService" + android:exported="false" + android:permission="android.permission.BIND_JOB_SERVICE"> + <intent-filter> + <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" /> + </intent-filter> + </service> + + + <receiver android:name=".services.BootReceiver"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED"/> + </intent-filter> + </receiver> + + <receiver android:name=".services.notification.NotificationReceiver"> + </receiver> + <activity android:name=".TermActivity" /> <activity android:name=".SchedulingActivity"/> <activity android:name=".CitizenListActivity" /> @@ -57,7 +87,11 @@ <activity android:name=".RetrievalFailureActivity" /> <activity android:name=".RetrievalSuccessActivity" /> <activity android:name=".SchedulesHistoryActivity" /> - <activity android:name=".ScheduleInfoActivity"></activity> + <activity android:name=".ScheduleInfoActivity" /> + + + + </application> </manifest> \ No newline at end of file diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/CitizenListActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/CitizenListActivity.java index 5c337c5d68e9cff2d81778ce090e29b39676e4af..055b3490577ffe01fa00666073ea9fae21b4c035 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/CitizenListActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/CitizenListActivity.java @@ -167,7 +167,9 @@ public class CitizenListActivity extends AppCompatActivity implements CitizenVie @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); - getActivity().onBackPressed(); + Activity activity = getActivity(); + if(activity != null) + activity.onBackPressed(); } } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/HomeActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/HomeActivity.java index d791835215fe65ef2946ef24b6302241f578ef5a..2c42be76016d9fd58fc545c9aa5609d8a21821e6 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/HomeActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/HomeActivity.java @@ -21,6 +21,7 @@ import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.Toolbar; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; +import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; @@ -29,11 +30,18 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.List; + import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; import br.ufpr.c3sl.agendador.agendador.helpers.UserImgHelper; import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.Notification; import br.ufpr.c3sl.agendador.agendador.presenters.HomePresenter; import br.ufpr.c3sl.agendador.agendador.presenters.PresenterManager; +import br.ufpr.c3sl.agendador.agendador.services.notification.LocalNotificationManager; import br.ufpr.c3sl.agendador.agendador.views.HomeView; /** @@ -171,6 +179,14 @@ public class HomeActivity extends AppCompatActivity implements HomeView, }); changeTextColor(); showSnackBar(getIntent()); + + + /* + Notification notification = new Notification(1, "2017-12-18T10:10:00.000-0200", false, "Alo Alo", "a@a.c", false); + Log.d("Agendador", "Criando notificação"); + LocalNotificationManager.createLocalNotification(this, notification, osb, new Gson()); + */ + } @TargetApi(23) diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/LoginActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/LoginActivity.java index 1842ca22e6dc76562bdc4bde6491d6991dda9364..aa5b99e7d088e5814b8a0f8f000e0027dfbdf6af 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/LoginActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/LoginActivity.java @@ -12,6 +12,7 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.TextWatcher; import android.text.style.StyleSpan; +import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; @@ -19,6 +20,14 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import com.firebase.jobdispatcher.Constraint; +import com.firebase.jobdispatcher.FirebaseJobDispatcher; +import com.firebase.jobdispatcher.GooglePlayDriver; +import com.firebase.jobdispatcher.Job; +import com.firebase.jobdispatcher.Lifetime; +import com.firebase.jobdispatcher.RetryStrategy; +import com.firebase.jobdispatcher.Trigger; + import java.io.IOException; import br.ufpr.c3sl.agendador.agendador.helpers.ConnectionChecker; @@ -28,6 +37,7 @@ import br.ufpr.c3sl.agendador.agendador.helpers.UserImgHelper; import br.ufpr.c3sl.agendador.agendador.helpers.Utils; import br.ufpr.c3sl.agendador.agendador.presenters.LoginPresenter; import br.ufpr.c3sl.agendador.agendador.presenters.PresenterManager; +import br.ufpr.c3sl.agendador.agendador.services.notification.NotificationsListService; import br.ufpr.c3sl.agendador.agendador.views.LoginView; /** @@ -205,6 +215,8 @@ public class LoginActivity extends AppCompatActivity implements LoginView { @Override public void afterSuccessfulLogin() { + + presenter.createServiceLocalNotificationsList(); Intent intent = new Intent(LoginActivity.this, HomeActivity.class); startActivity(intent); finish(); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleConfirmationActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleConfirmationActivity.java index ff28cb4ff86641782f089e13f4a2ef534caf3861..8907f25ea69a88553595f49066c53527194a2fad 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleConfirmationActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleConfirmationActivity.java @@ -6,6 +6,7 @@ import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.graphics.Color; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; @@ -15,26 +16,35 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.view.menu.MenuBuilder; import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.ArrayAdapter; import android.widget.Button; +import android.widget.EditText; import android.widget.ImageView; import android.widget.ProgressBar; +import android.widget.RadioButton; +import android.widget.Spinner; import android.widget.TextView; +import android.widget.Toast; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Locale; import br.ufpr.c3sl.agendador.agendador.helpers.ConnectionErrorDialog; +import br.ufpr.c3sl.agendador.agendador.helpers.DateValidator; import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; import br.ufpr.c3sl.agendador.agendador.helpers.UserImgHelper; import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.helpers.listeners.SpinnerActivity; +import br.ufpr.c3sl.agendador.agendador.helpers.listeners.SpinnerListener; import br.ufpr.c3sl.agendador.agendador.models.CitizenCompact; +import br.ufpr.c3sl.agendador.agendador.models.Notification; import br.ufpr.c3sl.agendador.agendador.models.ScheduleConfirmation; -import br.ufpr.c3sl.agendador.agendador.models.UserOutput; import br.ufpr.c3sl.agendador.agendador.presenters.PresenterManager; import br.ufpr.c3sl.agendador.agendador.presenters.ScheduleConfirmationPresenter; import br.ufpr.c3sl.agendador.agendador.views.ScheduleConfirmationView; @@ -44,7 +54,9 @@ import br.ufpr.c3sl.agendador.agendador.views.ScheduleConfirmationView; */ -public class ScheduleConfirmationActivity extends AppCompatActivity implements ScheduleConfirmationView, NavigationView.OnNavigationItemSelectedListener{ +public class ScheduleConfirmationActivity extends AppCompatActivity implements ScheduleConfirmationView, NavigationView.OnNavigationItemSelectedListener, SpinnerActivity{ + + private int[] reminderTimeArray; private ScheduleConfirmationPresenter scheduleConfirmationPresenter; @@ -58,40 +70,52 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S private ProgressBar pb_scheduleConfirmation; + private EditText et_email; + + private TextView tv_email_warning, tv_email; + private Button bt_schedule, bt_back; + private static final String SPINNER_POS = "spinner_position"; + + private Spinner sp_notification; + private static final String EXIT_CONFIRMATION_DIALOG_TAG = "exit_dialog"; + private RadioButton rb_yes, rb_no; + private long id; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if(savedInstanceState == null){ - scheduleConfirmationPresenter = new ScheduleConfirmationPresenter(getBaseContext()); - }else{ - scheduleConfirmationPresenter = PresenterManager.getInstance().restorePresenter(savedInstanceState); - if(scheduleConfirmationPresenter == null) - scheduleConfirmationPresenter = new ScheduleConfirmationPresenter(this); - } setContentView(R.layout.activity_schedule_confirmation); Bundle bundle = getIntent().getBundleExtra(Utils.SCHEDULE_BUNDLE); final CitizenCompact citizen = bundle.getParcelable(Utils.CITIZEN); final ScheduleConfirmation scheduleConfirmation = bundle.getParcelable(Utils.SCHEDULE_CONFIRMATION); - String sectorName = scheduleConfirmation.getSectorName(); - String locationName = scheduleConfirmation.getLocationName(); - String typeName = scheduleConfirmation.getServiceTypeName(); - String address = scheduleConfirmation.getAddressStreet() + ", " + scheduleConfirmation.getAddressNumber(); + String sectorName = ""; + String locationName = ""; + String typeName = ""; + String address = ""; + if (scheduleConfirmation != null) { + sectorName = scheduleConfirmation.getSectorName(); + locationName = scheduleConfirmation.getLocationName(); + typeName = scheduleConfirmation.getServiceTypeName(); + address = scheduleConfirmation.getAddressStreet() + ", " + scheduleConfirmation.getAddressNumber(); + }else{ + Log.e("Agendador", "Bundle chegou vazio ou Schedule confirmation null " + + "ou citizenCompact null na classe ScheduleConfirmationActivity"); + } Toolbar toolbar = (Toolbar) findViewById(R.id.agendador_toolbar); toolbar.setTitle(null); setSupportActionBar(toolbar); ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(this, "Agendador", Context.MODE_PRIVATE); - String citizenName = osb.getString("name", null); - String citizenCity = osb.getString("city.name", null); - id = osb.getLong("id", 0); + String citizenName = osb.getString(Utils.NAME, null); + String citizenCity = osb.getString(Utils.CITY_NAME, null); + id = osb.getLong(Utils.ID, 0); ActionMenuView actionMenuView = (ActionMenuView) toolbar.findViewById(R.id.agendador_toolbar_menu); menuBuilder = (MenuBuilder) actionMenuView.getMenu(); @@ -146,6 +170,13 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S navigationView.setNavigationItemSelectedListener(this); + + String[] notificationTime = getResources().getStringArray(R.array.notification_time); + + sp_notification = (Spinner) findViewById(R.id.spnr_schedule_time); + + sp_notification.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, notificationTime)); + pb_scheduleConfirmation = (ProgressBar) findViewById(R.id.pb_schedule_confirmation); TextView tv_citizen = (TextView) findViewById(R.id.tv_schedule_citizen_content); @@ -154,7 +185,9 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S TextView tv_type = (TextView) findViewById(R.id.tv_schedule_type_content); TextView tv_schedule = (TextView) findViewById(R.id.tv_schedule_date_content); TextView tv_street = (TextView) findViewById(R.id.tv_schedule_street_content); - + et_email = (EditText) findViewById(R.id.et_schedule_email); + tv_email_warning = (TextView) findViewById(R.id.tv_schedule_email_warning); + final EditText et_scheduleNote = (EditText) findViewById(R.id.et_schedule_note); tv_sector.setText(sectorName); tv_citizen.setText(citizen.getName()); @@ -172,9 +205,73 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S bt_schedule.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - scheduleConfirmationPresenter.onScheduleClicked(scheduleConfirmation, citizen); - bt_schedule.setEnabled(false); - bt_back.setEnabled(false); + boolean schedule = true; + if(rb_yes.isChecked()){ + if(!DateValidator.isValidEmail(et_email.getText().toString())){ + Toast.makeText(getBaseContext(), getResources().getString(R.string.not_valid_email), Toast.LENGTH_SHORT).show(); + schedule = false; + tv_email_warning.setVisibility(View.VISIBLE); + }else{ + tv_email_warning.setVisibility(View.INVISIBLE); + } + } + + if(schedule){ + + SimpleDateFormat sdfServer = new SimpleDateFormat(Utils.SERVER_FORMAT, Locale.getDefault()); + SimpleDateFormat sdfDate = new SimpleDateFormat(Utils.DATE_FORMAT, Locale.getDefault()); + SimpleDateFormat sdfHour = new SimpleDateFormat(Utils.HOUR_FORMAT, Locale.getDefault()); + + + /* + * Position zero is "don't notify" + */ + Notification notification = null; + if(sp_notification.getSelectedItemPosition() != reminderTimeArray.length - 1) { + notification = new Notification(); + notification.scheduleId = scheduleConfirmation.getId(); + notification.emailSent = false; + + notification.description = getResources().getString(R.string.notification_content, sdfDate.format(scheduleConfirmation.getStartTime()), sdfHour.format(scheduleConfirmation.getStartTime()), scheduleConfirmation.getServiceTypeName(), + scheduleConfirmation.getLocationName(), scheduleConfirmation.getAddressStreet(), scheduleConfirmation.getAddressNumber()); + + + notification.reminderTime = sdfServer.format(scheduleConfirmation.getStartTime().getTime() - (reminderTimeArray[sp_notification.getSelectedItemPosition()])); + + if (rb_yes.isChecked()) { + notification.emailAddress = et_email.getText().toString(); + } + } + + scheduleConfirmationPresenter.onScheduleClicked(scheduleConfirmation, citizen, et_scheduleNote.getText().toString(), notification); + bt_schedule.setEnabled(false); + bt_back.setEnabled(false); + } + + } + }); + + tv_email = (TextView) findViewById(R.id.tv_schedule_email); + + rb_yes = (RadioButton) findViewById(R.id.rb_notify_email_yes); + + rb_yes.setChecked(true); + + rb_no = (RadioButton) findViewById(R.id.rb_notify_email_no); + + rb_yes.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //Radio Button already toggled from user click. + radioButtonNotificationSelected(R.id.rb_notify_email_yes, false); + } + }); + + rb_no.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //Radio Button already toggled from user click. + radioButtonNotificationSelected(R.id.rb_notify_email_no, false); } }); @@ -184,6 +281,21 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S onBackPressed(); } }); + + if(savedInstanceState == null){ + scheduleConfirmationPresenter = new ScheduleConfirmationPresenter(this); + }else{ + scheduleConfirmationPresenter = PresenterManager.getInstance().restorePresenter(savedInstanceState); + if(scheduleConfirmationPresenter == null) + scheduleConfirmationPresenter = new ScheduleConfirmationPresenter(this); + } + + + SpinnerListener notificationListener = new SpinnerListener(this); + sp_notification.setOnItemSelectedListener(notificationListener); + sp_notification.setOnTouchListener(notificationListener); + + reminderTimeArray = getResources().getIntArray(R.array.notification_time_milis); } @Override @@ -202,6 +314,7 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S @Override public void onSaveInstanceState(Bundle outState) { PresenterManager.getInstance().savePresenter(scheduleConfirmationPresenter, outState); + outState.putInt(SPINNER_POS, sp_notification.getSelectedItemPosition()); super.onSaveInstanceState(outState); } @@ -279,13 +392,39 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S } } - public void onSuccess(){ + + + + private void radioButtonNotificationSelected(int id, boolean toggle){ + switch (id){ + case R.id.rb_notify_email_no: + if(toggle) { + rb_no.toggle(); + } + et_email.setEnabled(false); + et_email.setText(""); + tv_email.setTextColor(Color.GRAY); + tv_email_warning.setVisibility(View.INVISIBLE); + break; + case R.id.rb_notify_email_yes: + if(toggle){ + rb_yes.toggle(); + } + et_email.setEnabled(true); + tv_email.setTextColor(Color.BLACK); + break; + } + } + + @Override + public void returnHome(){ Intent intent = new Intent(ScheduleConfirmationActivity.this, HomeActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); Bundle bundle = new Bundle(); bundle.putInt(HomeActivity.SHOW_SCHEDULE_SNACKBAR, 1); intent.putExtra(HomeActivity.SHOW_SCHEDULE_SNACKBAR,bundle); startActivity(intent); + finish(); } @Override @@ -298,7 +437,6 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S bt_schedule.setEnabled(true); bt_back.setEnabled(true); } - } @Override @@ -314,6 +452,22 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S } } + @Override + public void updateSpinners(int id) { + switch (id){ + case R.id.spnr_schedule_time: + if(sp_notification.getSelectedItemPosition() == reminderTimeArray.length - 1){ + radioButtonNotificationSelected(R.id.rb_notify_email_no, true); + rb_no.setEnabled(false); + rb_yes.setEnabled(false); + }else{ + rb_no.setEnabled(true); + rb_yes.setEnabled(true); + } + break; + } + } + public static class ConfirmationDialog extends DialogFragment { @Override diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleInfoActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleInfoActivity.java index 5c8a9102fe4ce2010a70ac5e9d4189283d59ad98..51285657fa0e9c5060701938e56cc069737c003d 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleInfoActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/ScheduleInfoActivity.java @@ -20,6 +20,7 @@ import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.ImageView; +import android.widget.ProgressBar; import android.widget.TextView; import org.w3c.dom.Text; @@ -28,12 +29,17 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Locale; +import br.ufpr.c3sl.agendador.agendador.helpers.ConnectionErrorDialog; import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; import br.ufpr.c3sl.agendador.agendador.helpers.UserImgHelper; import br.ufpr.c3sl.agendador.agendador.helpers.Utils; import br.ufpr.c3sl.agendador.agendador.models.CheckSchedules; +import br.ufpr.c3sl.agendador.agendador.presenters.PresenterManager; +import br.ufpr.c3sl.agendador.agendador.presenters.ScheduleConfirmationPresenter; +import br.ufpr.c3sl.agendador.agendador.presenters.ScheduleInfoPresenter; +import br.ufpr.c3sl.agendador.agendador.views.ScheduleInfoView; -public class ScheduleInfoActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{ +public class ScheduleInfoActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, ScheduleInfoView{ private MenuBuilder menuBuilder; @@ -43,12 +49,22 @@ public class ScheduleInfoActivity extends AppCompatActivity implements Navigatio private long id; - private CheckSchedules.Schedule schedule; - private static final String EXIT_CONFIRMATION_DIALOG_TAG = "exit_dialog"; private int caller; + private TextView tv_name, tv_sector, tv_location, tv_type, + tv_date, tv_address, tv_situation, tv_situation_content; + + private ScheduleInfoPresenter presenter; + + private ConnectionErrorDialog connectionErrorDialog; + + private Button bt_back; + + private ProgressBar pb_scheduleInfo; + + private boolean hasToRequest; @Override protected void onCreate(Bundle savedInstanceState) { @@ -114,29 +130,47 @@ public class ScheduleInfoActivity extends AppCompatActivity implements Navigatio } }); + tv_name = (TextView) findViewById(R.id.tv_schedule_citizen_content); + tv_sector = (TextView) findViewById(R.id.tv_schedule_sector_content); + tv_location = (TextView) findViewById(R.id.tv_schedule_location_content); + tv_type = (TextView) findViewById(R.id.tv_schedule_type_content); + tv_date = (TextView) findViewById(R.id.tv_schedule_date_content); + tv_address = (TextView) findViewById(R.id.tv_schedule_street_content); + tv_situation = (TextView) findViewById(R.id.tv_schedule_situation); + tv_situation_content = (TextView) findViewById(R.id.tv_schedule_situation_content); + pb_scheduleInfo = (ProgressBar) findViewById(R.id.pb_schedule_info); + bt_back = (Button) findViewById(R.id.btn_schedule_info_back); + bt_back.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onBackPressed(); + } + }); + + id = getIntent().getIntExtra(Utils.ID, 0); + + + if(savedInstanceState == null){ + presenter = new ScheduleInfoPresenter(this); + }else{ + presenter = PresenterManager.getInstance().restorePresenter(savedInstanceState); + if(presenter == null) { + presenter = new ScheduleInfoPresenter(this); + } + } + + Bundle bundle = getIntent().getBundleExtra(Utils.SCHEDULE_BUNDLE); - if(bundle != null){ - DateFormat df = new SimpleDateFormat("dd/MM/yyyy - HH:mm", Locale.getDefault()); + CheckSchedules.Schedule schedule = null; + + hasToRequest = true; + if(bundle != null && bundle.getParcelable(Utils.SCHEDULE) != null){ String name = bundle.getString(Utils.NAME); id = bundle.getLong(Utils.ID); schedule = bundle.getParcelable(Utils.SCHEDULE); - TextView tv_name = (TextView) findViewById(R.id.tv_schedule_citizen_content); - TextView tv_sector = (TextView) findViewById(R.id.tv_schedule_sector_content); - TextView tv_location = (TextView) findViewById(R.id.tv_schedule_location_content); - TextView tv_type = (TextView) findViewById(R.id.tv_schedule_type_content); - TextView tv_date = (TextView) findViewById(R.id.tv_schedule_date_content); - TextView tv_address = (TextView) findViewById(R.id.tv_schedule_street_content); - TextView tv_situation = (TextView) findViewById(R.id.tv_schedule_situation); - TextView tv_situation_content = (TextView) findViewById(R.id.tv_schedule_situation_content); - tv_name.setText(name); - tv_sector.setText(schedule.getSectorName()); - tv_location.setText(schedule.getLocationName()); - tv_type.setText(schedule.getServiceTypeName()); - tv_date.setText(df.format(schedule.getStartTime())); - String address = schedule.getAddressStreet() + ", " + schedule.getAddressNumber(); - tv_address.setText(address); + showScheduleContent(schedule, null); caller = bundle.getInt(Utils.CALLER_ACTIVITY); if(caller == Utils.SCHEDULES_ACTIVITY){ @@ -147,22 +181,45 @@ public class ScheduleInfoActivity extends AppCompatActivity implements Navigatio navigationView.setCheckedItem(R.id.nav_schedules_history); tv_situation_content.setText(schedule.getSituation()); } + hasToRequest = false; } - navigationView.setNavigationItemSelectedListener(this); - Button bt_back = (Button) findViewById(R.id.btn_schedule_info_back); - bt_back.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onBackPressed(); - } - }); } + @Override + protected void onPause() { + super.onPause(); + presenter.unbindView(); + } + + @Override + protected void onResume() { + super.onResume(); + presenter.bindView(this); + if(hasToRequest && id != 0 ){ + presenter.requestScheduleInfo(id); + } + } + + // TODO: 16/01/18 extract date pattern to utils class + public void showScheduleContent(CheckSchedules.Schedule schedule, String citizenName){ + DateFormat df = new SimpleDateFormat("dd/MM/yyyy - HH:mm", Locale.getDefault()); + tv_sector.setText(schedule.getSectorName()); + tv_location.setText(schedule.getLocationName()); + tv_type.setText(schedule.getServiceTypeName()); + tv_date.setText(df.format(schedule.getStartTime())); + String address = schedule.getAddressStreet() + ", " + schedule.getAddressNumber(); + tv_address.setText(address); + tv_situation_content.setText(schedule.getSituation()); + if(citizenName != null && !citizenName.equals("")){ + tv_name.setText(citizenName); + } + } + @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { Intent intent; @@ -219,6 +276,42 @@ public class ScheduleInfoActivity extends AppCompatActivity implements Navigatio } } + @Override + public void setProgressBar(boolean enabled) { + if (enabled) { + pb_scheduleInfo.setVisibility(View.VISIBLE); + } else { + pb_scheduleInfo.setVisibility(View.INVISIBLE); + } + bt_back.setEnabled(!enabled); + } + + @Override + public void setConnectionError(boolean enabled) { + if(enabled){ + if(connectionErrorDialog == null){ + connectionErrorDialog = new ConnectionErrorDialog(); + } + Bundle bundle = new Bundle(); + bundle.putString(ConnectionErrorDialog.DIALOG_MESSAGE, getString(R.string.dialog_connection_error_message)); + connectionErrorDialog.setArguments(bundle); + connectionErrorDialog.show(getFragmentManager(), ConnectionErrorDialog.DIALOG_CONNECTION_ERROR); + } + } + + @Override + public void setNoConnection(boolean enabled) { + if(enabled){ + if(connectionErrorDialog == null){ + connectionErrorDialog = new ConnectionErrorDialog(); + } + Bundle bundle = new Bundle(); + bundle.putString(ConnectionErrorDialog.DIALOG_MESSAGE, getString(R.string.dialog_connection_error_message)); + connectionErrorDialog.setArguments(bundle); + connectionErrorDialog.show(getFragmentManager(), ConnectionErrorDialog.DIALOG_CONNECTION_ERROR); + } + } + public static class ConfirmationDialogFragment extends DialogFragment { @NonNull @Override @@ -229,9 +322,8 @@ public class ScheduleInfoActivity extends AppCompatActivity implements Navigatio builder.setMessage(R.string.signout_confirmation) .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - SchedulesActivity activity = (SchedulesActivity) getActivity(); - // TODO: 05/10/17 fix here - //activity.presenter.onSignOutClicked(); + ScheduleInfoActivity activity = (ScheduleInfoActivity) getActivity(); + activity.presenter.onSignOutClicked(); } }) .setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() { diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesActivity.java index c07d3ce1eea2e692f5f11ab1a7ee29b434239555..c1019168cc1e801fb48d8233b8ae1930a9e77f5d 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesActivity.java @@ -117,7 +117,17 @@ public class SchedulesActivity extends AppCompatActivity implements NavigationVi final DrawerLayout drawer; ActionMenuView actionMenuView = (ActionMenuView) toolbar.findViewById(R.id.agendador_toolbar_menu); menuBuilder = (MenuBuilder) actionMenuView.getMenu(); + menuBuilder.setCallback(new MenuBuilder.Callback() { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + return onOptionsItemSelected(item); + } + @Override + public void onMenuModeChange(MenuBuilder menu) { + + } + }); ImageView drawerHamburger = (ImageView) toolbar.findViewById(R.id.img_toolbar_hamburger); @@ -153,6 +163,7 @@ public class SchedulesActivity extends AppCompatActivity implements NavigationVi }); + navigationView.setCheckedItem(R.id.nav_check_schedules); navigationView.setNavigationItemSelectedListener(this); @@ -239,9 +250,9 @@ public class SchedulesActivity extends AppCompatActivity implements NavigationVi maxDate = new Date(); String date; - if(schedules.getSchedulesList() != null) { + if(schedules.getSchedulesModel().getSchedulesList() != null) { - for (CheckSchedules.Schedule schedule : schedules.getSchedulesList()) { + for (CheckSchedules.Schedule schedule : schedules.getSchedulesModel().getSchedulesList()) { date = dateFormat.format(schedule.getStartTime()); if(schedule.getStartTime().after(maxDate)){ maxDate = schedule.getStartTime(); @@ -261,7 +272,7 @@ public class SchedulesActivity extends AppCompatActivity implements NavigationVi for (CheckSchedules.DependentSchedules dependentSchedules : schedules.getDependentSchedulesList()) { - for (CheckSchedules.Schedule schedule : dependentSchedules.getSchedulesList()){ + for (CheckSchedules.Schedule schedule : dependentSchedules.getScheduleListModel().getSchedulesList()){ date = dateFormat.format(schedule.getStartTime()); if(schedule.getStartTime().after(maxDate)){ maxDate = schedule.getStartTime(); @@ -403,8 +414,8 @@ public class SchedulesActivity extends AppCompatActivity implements NavigationVi long id = 0; //searching if this schedule belongs to the citizen or the dependents. - for(int i = 0; i < schedules.getSchedulesList().size() && id == 0; ++i){ - if(schedules.getSchedulesList().get(i).getId() == selectedSchedule.getId()) + for(int i = 0; i < schedules.getSchedulesModel().getSchedulesList().size() && id == 0; ++i){ + if(schedules.getSchedulesModel().getSchedulesList().get(i).getId() == selectedSchedule.getId()) { id = schedules.getId(); name = schedules.getName(); @@ -412,8 +423,8 @@ public class SchedulesActivity extends AppCompatActivity implements NavigationVi } for (int i = 0; i < schedules.getDependentSchedulesList().size() && id == 0; ++i){ - for(int j = 0; j < schedules.getDependentSchedulesList().get(i).getSchedulesList().size() && id == 0; ++j){ - if(schedules.getDependentSchedulesList().get(i).getSchedulesList().get(j).getId() == selectedSchedule.getId()) + for(int j = 0; j < schedules.getDependentSchedulesList().get(i).getScheduleListModel().getSchedulesList().size() && id == 0; ++j){ + if(schedules.getDependentSchedulesList().get(i).getScheduleListModel().getSchedulesList().get(j).getId() == selectedSchedule.getId()) { id = schedules.getDependentSchedulesList().get(i).getId(); name = schedules.getDependentSchedulesList().get(i).getName(); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesHistoryActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesHistoryActivity.java index c580f3567f63d484dbd8b0926dc8ce0e2da0da9e..aa2aea84cf6a99961ad1eb912f484a4318d2ebe6 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesHistoryActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulesHistoryActivity.java @@ -39,7 +39,7 @@ import br.ufpr.c3sl.agendador.agendador.helpers.UserImgHelper; import br.ufpr.c3sl.agendador.agendador.helpers.Utils; import br.ufpr.c3sl.agendador.agendador.helpers.adapters.HintAdapter; import br.ufpr.c3sl.agendador.agendador.helpers.adapters.SchedulesExpandableListAdapter; -import br.ufpr.c3sl.agendador.agendador.helpers.listeners.schedulesHistory.FilterListener; +import br.ufpr.c3sl.agendador.agendador.helpers.listeners.SpinnerListener; import br.ufpr.c3sl.agendador.agendador.models.CheckSchedules; import br.ufpr.c3sl.agendador.agendador.models.FormFilters; import br.ufpr.c3sl.agendador.agendador.models.ServicePlace; @@ -47,10 +47,10 @@ import br.ufpr.c3sl.agendador.agendador.models.ServiceType; import br.ufpr.c3sl.agendador.agendador.presenters.PresenterManager; import br.ufpr.c3sl.agendador.agendador.presenters.SchedulesHistoryPresenter; import br.ufpr.c3sl.agendador.agendador.views.SchedulesHistoryView; +import br.ufpr.c3sl.agendador.agendador.helpers.listeners.SpinnerActivity; - -public class SchedulesHistoryActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, SchedulesHistoryView { +public class SchedulesHistoryActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, SchedulesHistoryView, SpinnerActivity { private NavigationView navigationView; private MenuBuilder menuBuilder; @@ -65,7 +65,6 @@ public class SchedulesHistoryActivity extends AppCompatActivity implements Navig private FormFilters filters; private long[] sectorsIdArray, serviceTypesIdArray, servicePlacesIdArray, situationIdArray; private Button bt_search, bt_clear; - private FilterListener filterListener; private static final String EXIT_CONFIRMATION_DIALOG_TAG = "exit_dialog"; private static final String SPINNER_SECTOR = "spinner_setor"; private static final String SPINNER_TYPE = "spinner_type"; @@ -242,7 +241,7 @@ public class SchedulesHistoryActivity extends AppCompatActivity implements Navig } }); - filterListener = new FilterListener(this); + SpinnerListener filterListener = new SpinnerListener(this); sp_sector.setOnItemSelectedListener(filterListener); sp_sector.setOnTouchListener(filterListener); @@ -757,11 +756,11 @@ public class SchedulesHistoryActivity extends AppCompatActivity implements Navig expandableListTitle = new ArrayList<>(); expandableListTitle.add(new Pair<>(schedules.getName(), schedules.getId())); - expandableListDetail.put(schedules.getName(), schedules.getSchedulesList()); + expandableListDetail.put(schedules.getName(), schedules.getSchedulesModel().getSchedulesList()); for (CheckSchedules.DependentSchedules dependentSchedules : schedules.getDependentSchedulesList()) { expandableListTitle.add(new Pair<>(dependentSchedules.getName(), dependentSchedules.getId())); - expandableListDetail.put(dependentSchedules.getName(), dependentSchedules.getSchedulesList()); + expandableListDetail.put(dependentSchedules.getName(), dependentSchedules.getScheduleListModel().getSchedulesList()); } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulingActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulingActivity.java index 363badcbcb3d3ea69fd2aedd9856357e8ab95f7d..7b996aecbe4a30deb6a8787c7987b1591ad64126 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulingActivity.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/SchedulingActivity.java @@ -49,9 +49,9 @@ import br.ufpr.c3sl.agendador.agendador.helpers.adapters.HintAdapter; import br.ufpr.c3sl.agendador.agendador.helpers.adapters.HintEnableAdapter; import br.ufpr.c3sl.agendador.agendador.helpers.decorators.NotAvailableDecorator; import br.ufpr.c3sl.agendador.agendador.helpers.decorators.SchedulesDecorator; -import br.ufpr.c3sl.agendador.agendador.helpers.listeners.SectorSpinnerListener; -import br.ufpr.c3sl.agendador.agendador.helpers.listeners.ServicePlaceSpinnerListener; -import br.ufpr.c3sl.agendador.agendador.helpers.listeners.ServiceTypeSpinnerListener; +import br.ufpr.c3sl.agendador.agendador.helpers.listeners.scheduling.SectorSpinnerListener; +import br.ufpr.c3sl.agendador.agendador.helpers.listeners.scheduling.ServicePlaceSpinnerListener; +import br.ufpr.c3sl.agendador.agendador.helpers.listeners.scheduling.ServiceTypeSpinnerListener; import br.ufpr.c3sl.agendador.agendador.models.CitizenCompact; import br.ufpr.c3sl.agendador.agendador.models.ScheduleConfirmation; import br.ufpr.c3sl.agendador.agendador.models.SectorInput; @@ -328,6 +328,7 @@ public class SchedulingActivity extends AppCompatActivity implements SchedulingV mcv.setSelectionMode(MaterialCalendarView.SELECTION_MODE_NONE); + // TODO: 12/12/17 check here dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); hourFormat = new SimpleDateFormat("HH:mm", Locale.getDefault()); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/Utils.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/Utils.java index 9a1db72110307c5d2e667a75e2f3c5eabe7f0320..dee274fd13b15f0b99a70d18b96c1b49bdf52347 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/Utils.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/Utils.java @@ -34,6 +34,14 @@ import br.ufpr.c3sl.agendador.agendador.models.UserOutput; public abstract class Utils { + public static final String DATE_HOUR_FORMAT = "dd/MM/yyyy - HH:mm"; + + public static final String HOUR_FORMAT = "HH:mm"; + + public static final String DATE_FORMAT = "dd/MM/yyyy"; + + public static final String SERVER_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + public final static String SERVICE_UPDATE_IMAGE = "br.c3sl.ufpr.image_update"; public static final String SECTORS_LIST = "sectors_list"; @@ -50,10 +58,10 @@ public abstract class Utils { public static final String SCHEDULE_CONFIRMATION = "selected_schedule"; - public static final String CITIZEN = "citizen_object"; + public static final String SIZE_LARGE = "large"; + public static final String CITIZEN = "citizen_object"; public static final String CITIZEN_BUNDLE = "br.ufpr.c3sl.agendador.agendador.citizen_bundle"; - public static final String JOB_FILE_NAME = "fileName_tag"; public static final String ADDRESS_NEIGHBORHOOD = "address.neighborhood"; public static final String ADDRESS_NUMBER = "address_number"; @@ -99,15 +107,29 @@ public abstract class Utils { public static final String CPF_MASK = "###.###.###-##"; public static final String BIRTH_DAY_MASK = "##/##/####"; public static final String SCHEDULE = "schedule"; - public static final String DATE_FORMAT = "MM/dd/yyyy HH:mm"; public static final String CALLER_ACTIVITY = "from_activity"; + + public static final int HISTORY_ACTIVITY = 1; public static final int SCHEDULES_ACTIVITY = 0; + + public static final String REQUEST_FILTER_SECTOR = "q[sector_id]"; public static final String REQUEST_FILTER_TYPE = "q[service_type_id]"; public static final String REQUEST_FILTER_PLACE = "q[service_place_id]"; public static final String REQUEST_FILTER_SITUATION = "q[situation_id]"; - public static final String SIZE_LARGE = "large"; + + + public static final String NOTIFICATION = "notification"; + public static final String NOTIFICATION_SERVICE = "br.c3sl.ufpr.notification_service"; + public static final String NOTIFICATION_UPDATE_LIST_SERVICE = "br.c3sl.ufpr.notification_update_service"; + public static final String NOTIFICATION_READ_SERVICE = "br.c3sl.ufpr.notification_read_service"; + public static final String NOTIFICATION_LIST = "notification_list"; + + + public static final String ACTION_SHOW_NOTIFICATION = "br.ufpr.c3sl.agendador.SHOW_NOTIFICATION"; + + public static final int SHOW_SCHEDULE_INFO_ACTION = 0x11; public static int getPixelValue(int dp, Context context) { Resources resources = context.getResources(); @@ -170,9 +192,9 @@ public abstract class Utils { String s = calendar.get(Calendar.YEAR) + ""; tv_calendarHeaderYear.setText(s); - - tv_calendarHeaderDayHour.setText(days_short[calendar.get(Calendar.DAY_OF_WEEK) - 1] + ", " + - months_short[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.DAY_OF_MONTH)); + String text = days_short[calendar.get(Calendar.DAY_OF_WEEK) - 1] + ", " + + months_short[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.DAY_OF_MONTH); + tv_calendarHeaderDayHour.setText(text); } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/adapters/SchedulesExpandableListAdapter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/adapters/SchedulesExpandableListAdapter.java index 91db747d2c3dce3e3bf1c2e2736db2a2e5919726..624d9a132170e79575e8eba38744ff418f6ea79b 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/adapters/SchedulesExpandableListAdapter.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/adapters/SchedulesExpandableListAdapter.java @@ -37,7 +37,7 @@ public class SchedulesExpandableListAdapter extends BaseExpandableListAdapter { this.context = context; this.expandableListTitle = expandableListTitle; this.expandableListDetail = expandableListDetail; - dateFormat = new SimpleDateFormat(Utils.DATE_FORMAT, Locale.getDefault()); + dateFormat = new SimpleDateFormat(Utils.DATE_HOUR_FORMAT, Locale.getDefault()); } @Override diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SpinnerActivity.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SpinnerActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..449c73777d792fb681bf5f42b4ba51b0e1526502 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SpinnerActivity.java @@ -0,0 +1,10 @@ +package br.ufpr.c3sl.agendador.agendador.helpers.listeners; + +/** + * Created by Lucas Braz Cunha on 22/11/17. + */ + +public interface SpinnerActivity { + + void updateSpinners(int id); +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/schedulesHistory/FilterListener.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SpinnerListener.java similarity index 62% rename from app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/schedulesHistory/FilterListener.java rename to app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SpinnerListener.java index c577ea3a7ef8881d9959f30f3d12e06f41e5bd4d..ea90e52efb6d4c0db75dec8da37737b365e0e78c 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/schedulesHistory/FilterListener.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SpinnerListener.java @@ -1,21 +1,19 @@ -package br.ufpr.c3sl.agendador.agendador.helpers.listeners.schedulesHistory; +package br.ufpr.c3sl.agendador.agendador.helpers.listeners; import android.view.MotionEvent; import android.view.View; import android.widget.AdapterView; -import br.ufpr.c3sl.agendador.agendador.views.SchedulesHistoryView; - /** - * Created by Lucas B. Cunha on 30/10/17. + * Created by Lucas Braz Cunha on 22/11/17. */ -public class FilterListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener { +public class SpinnerListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener { private boolean userSelect; - private SchedulesHistoryView activity; + private SpinnerActivity activity; - public FilterListener(SchedulesHistoryView activity) { + public SpinnerListener(SpinnerActivity activity) { this.activity = activity; this.userSelect = false; } @@ -23,6 +21,7 @@ public class FilterListener implements AdapterView.OnItemSelectedListener, View. @Override public boolean onTouch(View v, MotionEvent event) { userSelect = true; + v.performClick(); return false; } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SectorSpinnerListener.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/SectorSpinnerListener.java similarity index 96% rename from app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SectorSpinnerListener.java rename to app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/SectorSpinnerListener.java index 245aa1ffbbc9b0f22da9aecf4244e67818ba6610..6132923e831faa00fa888ffee8bb60262e66818c 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/SectorSpinnerListener.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/SectorSpinnerListener.java @@ -1,4 +1,4 @@ -package br.ufpr.c3sl.agendador.agendador.helpers.listeners; +package br.ufpr.c3sl.agendador.agendador.helpers.listeners.scheduling; import android.view.MotionEvent; import android.view.View; @@ -53,6 +53,7 @@ public class SectorSpinnerListener implements AdapterView.OnItemSelectedListener @Override public boolean onTouch(View v, MotionEvent event) { userSelect = true; + v.performClick(); return false; } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/ServicePlaceSpinnerListener.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/ServicePlaceSpinnerListener.java similarity index 96% rename from app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/ServicePlaceSpinnerListener.java rename to app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/ServicePlaceSpinnerListener.java index 0733222fa90bfaa9ec6904fbec7f45efb4d44170..92528323c908d106f7ebd2e65317a77ee334a3ef 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/ServicePlaceSpinnerListener.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/ServicePlaceSpinnerListener.java @@ -1,4 +1,4 @@ -package br.ufpr.c3sl.agendador.agendador.helpers.listeners; +package br.ufpr.c3sl.agendador.agendador.helpers.listeners.scheduling; import android.view.MotionEvent; import android.view.View; @@ -48,12 +48,13 @@ public class ServicePlaceSpinnerListener implements AdapterView.OnItemSelectedLi @Override public boolean onTouch(View v, MotionEvent event) { userSelect = true; + v.performClick(); return false; } @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { - if (userSelect && servicePlaces != null && servicePlaces.size() > 0 ) { + if (userSelect && servicePlaces != null && servicePlaces.size() > 0) { mcv.setSelectionMode(MaterialCalendarView.SELECTION_MODE_SINGLE); //if the position is the hint. diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/ServiceTypeSpinnerListener.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/ServiceTypeSpinnerListener.java similarity index 96% rename from app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/ServiceTypeSpinnerListener.java rename to app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/ServiceTypeSpinnerListener.java index 6d69eee254e85b5c1fdf6ce659544e9b93870b28..1601455fd71b56a1967c6a8c4be97a9b6d53778c 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/ServiceTypeSpinnerListener.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/helpers/listeners/scheduling/ServiceTypeSpinnerListener.java @@ -1,6 +1,5 @@ -package br.ufpr.c3sl.agendador.agendador.helpers.listeners; +package br.ufpr.c3sl.agendador.agendador.helpers.listeners.scheduling; -import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.AdapterView; @@ -53,12 +52,13 @@ public class ServiceTypeSpinnerListener implements AdapterView.OnItemSelectedLis @Override public boolean onTouch(View v, MotionEvent event) { userSelect = true; + v.performClick(); return false; } @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { - if (userSelect && serviceTypes != null && serviceTypes.size() > 0 ) { + if (userSelect && serviceTypes != null && serviceTypes.size() > 0) { presenter.requestAvailableSchedules(serviceTypes.get(pos).getId()); Utils.updateCalendarHeaderDate(mcv, tv_calendarHeaderYear, tv_calendarHeaderDayHour, days_short, months_short, null); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/CheckSchedules.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/CheckSchedules.java index 5549420b904822966208dcbb4a64d60c80217ef8..ad994dde2477b12f9e2c45037c3cd7bd6ac9bd60 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/CheckSchedules.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/CheckSchedules.java @@ -21,7 +21,7 @@ public class CheckSchedules { String name; @SerializedName("schedules") - private List<Schedule> schedulesList; + ScheduleListModel schedules; @SerializedName("dependants") private List<DependentSchedules> dependentSchedulesList; @@ -34,13 +34,17 @@ public class CheckSchedules { return name; } - public CheckSchedules(long id, String name, List<Schedule> schedulesList, List<DependentSchedules> dependentSchedulesList) { + public CheckSchedules(long id, String name, ScheduleListModel schedules, List<DependentSchedules> dependentSchedulesList) { this.id = id; this.name = name; - this.schedulesList = schedulesList; + this.schedules = schedules; this.dependentSchedulesList = dependentSchedulesList; } + public ScheduleListModel getSchedulesModel() { + return schedules; + } + public static class Schedule implements Parcelable{ @SerializedName("id") @@ -154,6 +158,11 @@ public class CheckSchedules { } + + public List<DependentSchedules> getDependentSchedulesList() { + return dependentSchedulesList; + } + public static class DependentSchedules{ @SerializedName("id") @@ -163,11 +172,7 @@ public class CheckSchedules { String name; @SerializedName("schedules") - List<Schedule> schedulesList; - - public List<Schedule> getSchedulesList() { - return schedulesList; - } + ScheduleListModel scheduleListModel; public long getId() { return id; @@ -176,20 +181,50 @@ public class CheckSchedules { public String getName() { return name; } - - public DependentSchedules(long id, String name, List<Schedule> scheduleList) { + public DependentSchedules(long id, String name) { this.id = id; this.name = name; - this.schedulesList = scheduleList; + } + + public ScheduleListModel getScheduleListModel() { + return scheduleListModel; } } - public List<Schedule> getSchedulesList() { - return schedulesList; + public static class ScheduleListModel { + + @SerializedName("num_entries") + int listSize; + + @SerializedName("entries") + List<Schedule> schedulesList; + + @SerializedName("sectors") + List<SectorScheduleModel> sectorScheduleModels; + + + public List<Schedule> getSchedulesList() { + return schedulesList; + } + + public int getListSize() { + return listSize; + } } - public List<DependentSchedules> getDependentSchedulesList() { - return dependentSchedulesList; + public static class SectorScheduleModel { + @SerializedName("id") + int sectorId; + + @SerializedName("name") + String name; + + @SerializedName("schedules_by_sector") + int chedulesbySector; + + @SerializedName("schedules") + int schedules; } + } \ No newline at end of file diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/Notification.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/Notification.java new file mode 100644 index 0000000000000000000000000000000000000000..be32a0ff9d73e34245655e9414213ad8cc68d555 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/Notification.java @@ -0,0 +1,89 @@ +package br.ufpr.c3sl.agendador.agendador.models; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Lucas Braz Cunha on 24/11/17. + */ + + +public class Notification implements Parcelable{ + + @SerializedName("schedule_id") + public long scheduleId; + + @SerializedName("reminder_time") + public String reminderTime; + + @SerializedName("read") + public boolean hasRead; + + @SerializedName("content") + public String description; + + @SerializedName("reminder_email") + public String emailAddress; + + @SerializedName("email_sent") + public boolean emailSent; + + + public Notification() { + } + + public Notification(long scheduleId, String reminderTime, boolean hasRead, String description, String emailAddress, boolean emailSent) { + this.scheduleId = scheduleId; + this.reminderTime = reminderTime; + this.hasRead = hasRead; + this.description = description; + this.emailAddress = emailAddress; + this.emailSent = emailSent; + } + + /**reads back fields IN THE ORDER they were written */ + private Notification(Parcel pc){ + scheduleId = pc.readLong(); + reminderTime = pc.readString(); + hasRead = pc.readByte() != 0; + description = pc.readString(); + emailAddress = pc.readString(); + emailSent = pc.readByte() != 0; + } + + /** Static field used to regenerate object, individually or as arrays */ + public static final Parcelable.Creator<Notification> CREATOR = new Parcelable.Creator<Notification>() { + public Notification createFromParcel(Parcel pc) { + return new Notification(pc); + } + public Notification[] newArray(int size) { + return new Notification[size]; + } + }; + + /** + * Flatten this object in to a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(scheduleId); + dest.writeString(reminderTime); + dest.writeByte((byte) (hasRead ? 1 : 0)); + dest.writeString(description); + dest.writeString(emailAddress); + dest.writeByte((byte) (emailSent ? 1 : 0)); + } + + @Override + public int describeContents() { + return 0; + } + + +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/NotificationReturn.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/NotificationReturn.java new file mode 100644 index 0000000000000000000000000000000000000000..c752e4b8b6822056f98d56b823249107d2718906 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/NotificationReturn.java @@ -0,0 +1,96 @@ +package br.ufpr.c3sl.agendador.agendador.models; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Lucas Braz Cunha on 18/12/17. + */ + +public class NotificationReturn implements Parcelable{ + + @SerializedName("id") + public long notificationId; + + @SerializedName("account_id") + public long accountId; + + @SerializedName("schedule_id") + public Long scheduleId; + + @SerializedName("resource_schedule_id") + public Long resourceScheduleId; + + @SerializedName("reminder_time") + public String reminderTime; + + @SerializedName("read") + public boolean hasRead; + + @SerializedName("content") + public String description; + + @SerializedName("reminder_email") + public String emailAddress; + + + //todo: Mudar para boolean quando o back-end atualizar + @SerializedName("reminder_email_sent") + public int emailSent; + + @SerializedName("created_at") + public String createdAt; + + @SerializedName("updated_at") + public String updatedAt; + + + + /**reads back fields IN THE ORDER they were written */ + private NotificationReturn(Parcel pc){ + notificationId = pc.readLong(); + accountId = pc.readLong(); + scheduleId = pc.readLong(); + resourceScheduleId = pc.readLong(); + reminderTime = pc.readString(); + hasRead = pc.readByte() != 0; + description = pc.readString(); + emailAddress = pc.readString(); + emailSent = pc.readInt(); + createdAt = pc.readString(); + updatedAt = pc.readString(); + } + + /** Static field used to regenerate object, individually or as arrays */ + public static final Parcelable.Creator<NotificationReturn> CREATOR = new Parcelable.Creator<NotificationReturn>() { + public NotificationReturn createFromParcel(Parcel pc) { + return new NotificationReturn(pc); + } + public NotificationReturn[] newArray(int size) { + return new NotificationReturn[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(notificationId); + dest.writeLong(accountId); + dest.writeLong(scheduleId == null ? 0 : scheduleId); + dest.writeLong(resourceScheduleId == null ? 0 : resourceScheduleId); + dest.writeString(reminderTime == null ? "" : reminderTime); + dest.writeByte((byte) (hasRead ? 1 : 0)); + dest.writeString(description == null ? "" : description); + dest.writeString(emailAddress == null ? "" : emailAddress); + dest.writeInt(emailSent); + dest.writeString(createdAt == null ? "" : createdAt); + dest.writeString(updatedAt == null ? "" : updatedAt); + } +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/NotificationUpdate.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/NotificationUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..5283f289a4347fb24389c8465e61eb1d8718fa32 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/NotificationUpdate.java @@ -0,0 +1,34 @@ +package br.ufpr.c3sl.agendador.agendador.models; + +import android.content.Context; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Lucas Braz Cunha on 22/01/18. + * + * Model used to update notification status to "read". + * + */ + +public class NotificationUpdate { + + @SerializedName("notification") + private NotificationUpdateContent content; + + + public NotificationUpdate(int hasRead){ + content = new NotificationUpdateContent(); + content.hasRead = hasRead; + } + + + private static class NotificationUpdateContent{ + + @SerializedName("read") + private int hasRead; + + } + +} + diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ScheduleInfo.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ScheduleInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..5c114d7cbcf3096b52abeed73b5046d0bdac596e --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ScheduleInfo.java @@ -0,0 +1,131 @@ +package br.ufpr.c3sl.agendador.agendador.models; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; + +/** + * Created by Lucas Braz Cunha on 16/01/18. + * + * model to receive request schedules/{id_schdule}?permission=citizen + * + * with complete schedule information. + */ + +public class ScheduleInfo { + + @SerializedName("citizen_data") + CitizenSchedule citizen; + + @SerializedName("service_place_data") + ServicePlaceSchedule servicePlace; + + @SerializedName("schedule_data") + ScheduleData schedule; + + + public long getId(){ + return citizen.id; + } + + public Date getStartTime(){ + return schedule.startTime; + } + + public String getSectorName(){ + return schedule.sectorName; + } + + public String getServiceTypeName(){ + return schedule.serviceTypeName; + } + + public String getLocationName(){ + return servicePlace.name; + } + + public String getAddressStreet(){ + return servicePlace.address.address; + } + + public String getAddressNumber(){ + return servicePlace.number; + } + + public String getSituation(){ + return schedule.situation; + } + + public String getCitizenName(){ + return citizen.name; + } + + + public static class CitizenSchedule{ + + @SerializedName("id") + long id; + + @SerializedName("name") + String name; + + @SerializedName("birth_date") + String birthDate; + + @SerializedName("rg") + String rg; + + @SerializedName("cpf") + String cpf; + + @SerializedName("note") + String note; + + } + + public static class ServicePlaceSchedule{ + + + @SerializedName("name") + String name; + + @SerializedName("complement") + String complement; + + @SerializedName("phone1") + String phone; + + @SerializedName("address_number") + String number; + + @SerializedName("address") + ShortAddress address; + + } + + public static class ScheduleData{ + + @SerializedName("id") + long id; + + @SerializedName("service_type_name") + String serviceTypeName; + + @SerializedName("service_start_time") + Date startTime; + + @SerializedName("professional_performer") + String professionalName; + + @SerializedName("situation") + String situation; + + @SerializedName("sector_name") + String sectorName; + + @SerializedName("sector_id") + long sectorId; + + } + +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ScheduleNote.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ScheduleNote.java index 3f394ccc981317af78d680e85af5ba2dc2a83919..05b6769fec2bb7e2f567bde55bc16107334658d9 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ScheduleNote.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ScheduleNote.java @@ -3,12 +3,14 @@ package br.ufpr.c3sl.agendador.agendador.models; import com.google.gson.annotations.SerializedName; /** - * Created by lbc16 on 19/09/17. + * Created by Lucas Braz Cunha on 19/09/17. */ public class ScheduleNote { - private Schedule schedule; + public Schedule schedule; + + public Notification notification; public ScheduleNote(String note) { this.schedule = new Schedule(note); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ShortAddress.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ShortAddress.java new file mode 100644 index 0000000000000000000000000000000000000000..7542ad087fde6dc24dce5e775dcf4fa4eea95ff6 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/models/ShortAddress.java @@ -0,0 +1,32 @@ +package br.ufpr.c3sl.agendador.agendador.models; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Lucas Braz Cunha on 16/01/18. + * + * model for request: schedules/{id_schedule}?permission=citizen + * + * + */ + +public class ShortAddress { + + @SerializedName("id") + long id; + + @SerializedName("zipcode") + String zipcode; + + @SerializedName("address") + String address; + + @SerializedName("neighborhood") + String neighborhood; + + @SerializedName("complement") + String complement; + + @SerializedName("complement2") + String complement2; +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiEndpoints.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiEndpoints.java index e2a8ed727c75d031689a70efd2d62fa1eb78d25e..268289f02f9aaa71c34888b1e267a32355ea40fc 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiEndpoints.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiEndpoints.java @@ -14,7 +14,11 @@ import br.ufpr.c3sl.agendador.agendador.models.Dependent; import br.ufpr.c3sl.agendador.agendador.models.DependentCreation; import br.ufpr.c3sl.agendador.agendador.models.FormFilters; import br.ufpr.c3sl.agendador.agendador.models.FullDependent; +import br.ufpr.c3sl.agendador.agendador.models.Notification; +import br.ufpr.c3sl.agendador.agendador.models.NotificationReturn; +import br.ufpr.c3sl.agendador.agendador.models.NotificationUpdate; import br.ufpr.c3sl.agendador.agendador.models.ScheduleConfirmation; +import br.ufpr.c3sl.agendador.agendador.models.ScheduleInfo; import br.ufpr.c3sl.agendador.agendador.models.ScheduleNote; import br.ufpr.c3sl.agendador.agendador.models.SectorInput; import br.ufpr.c3sl.agendador.agendador.models.ServicePlace; @@ -24,6 +28,7 @@ import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Body; import retrofit2.http.GET; +import retrofit2.http.PATCH; import retrofit2.http.POST; import retrofit2.http.PUT; import retrofit2.http.Path; @@ -84,12 +89,25 @@ public interface ApiEndpoints { @PUT("citizens/{id_user}/dependants/{id_dependent}?permission=citizen") Call<FullDependent> updateDependent(@Path("id_user") long citizenId, @Path("id_dependent") long dependentId, @Body DependentCreation dependent); - @GET("schedules?permission=citizen") + @GET("schedules?permission=citizen&history=true") Call<CheckSchedules> requestSchedulesHistory(@QueryMap Map<String, String> options); + @GET("schedules?permission=citizen&future=true") + Call<CheckSchedules> requestFutureSchedules(@QueryMap Map<String, String> options); @GET("forms/schedule_history?permission=citizen") Call<FormFilters> requestCitizenSectors(); + @POST("notifications?permission=citizen") + Call<NotificationReturn> requestCreateNotification(@Body Notification notification); + + @GET("schedules/{id_schedule}?permission=citizen") + Call<ScheduleInfo> requestScheduleInfo(@Path("id_schedule") long id); + + @GET("notifications?permission=citizen") + Call<List<NotificationReturn>> requestNotifications(); + + @PATCH("notifications/{notification_id}?permission=citizen") + Call<NotificationReturn> requestUpdateNotification(@Path("notification_id") long notificationId,@Body NotificationUpdate notification); } \ No newline at end of file diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiUtils.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiUtils.java index 3cc8490f0391dcba02019901a98271c2d7917649..8e35b8544c946f4eef404e2057dcb8feba959c29 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiUtils.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/network/ApiUtils.java @@ -14,8 +14,8 @@ import retrofit2.converter.gson.GsonConverterFactory; */ public abstract class ApiUtils { -// public static final String BASE_URL = "http://10.0.2.2:3000/v1/"; - public static final String BASE_URL = "http://newcastle.c3sl.ufpr.br/develop/v1/"; + public static final String BASE_URL = "http://10.0.2.2:3000/v1/"; +// public static final String BASE_URL = "http://newcastle.c3sl.ufpr.br/develop/v1/"; public static ApiEndpoints request(final Map<String, String> header) { OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/BasePresenter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/BasePresenter.java index 15b6e51bb8189ef3f063906984c919400c6a44f4..f93d6a039c2612e5b4ea583ebd36980b2433f9e7 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/BasePresenter.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/BasePresenter.java @@ -1,19 +1,32 @@ package br.ufpr.c3sl.agendador.agendador.presenters; +import android.app.AlarmManager; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.support.annotation.NonNull; +import android.util.Log; import com.firebase.jobdispatcher.FirebaseJobDispatcher; import com.firebase.jobdispatcher.GooglePlayDriver; +import com.google.gson.Gson; import java.lang.ref.WeakReference; +import java.util.List; import br.ufpr.c3sl.agendador.agendador.LoginActivity; import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.Notification; +import br.ufpr.c3sl.agendador.agendador.models.NotificationReturn; +import br.ufpr.c3sl.agendador.agendador.services.notification.LocalNotificationManager; +import br.ufpr.c3sl.agendador.agendador.services.notification.NotificationReceiver; import okhttp3.Headers; +import static android.content.Context.ALARM_SERVICE; + /** * Created by Bruno Freitas Tissei on 2/2/17. */ @@ -55,7 +68,7 @@ public abstract class BasePresenter<V> { protected void updateHeaders(Headers headers) { if(osb == null && context != null) - osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE);; + osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE); if(headers.get(Utils.ACCESS_TOKEN) != null) osb.edit().putString(Utils.ACCESS_TOKEN, headers.get(Utils.ACCESS_TOKEN)).apply(); @@ -65,19 +78,45 @@ public abstract class BasePresenter<V> { osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply(); if(headers.get(Utils.EXPIRY) != null) osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply(); - } public void onSignOutClicked() { FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context)); dispatcher.cancel(Utils.SERVICE_UPDATE_IMAGE); + dispatcher.cancel(Utils.NOTIFICATION_UPDATE_LIST_SERVICE); + + if(osb == null) + osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE); + + Gson gson = new Gson(); + + List<NotificationReturn> notificationList = LocalNotificationManager.getList(osb, gson); + + Intent intent = new Intent(context, NotificationReceiver.class); + intent.setAction(Utils.ACTION_SHOW_NOTIFICATION); + + PendingIntent pendingIntent; + AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE); + + + for(NotificationReturn n : notificationList){ + + pendingIntent = PendingIntent.getBroadcast(context, + LocalNotificationManager.NOTIFICATION_REMINDER_REQUEST_CODE + n.scheduleId.intValue(), intent, + PendingIntent.FLAG_CANCEL_CURRENT); + + if(am != null){ + am.cancel(pendingIntent); + } + + } osb.edit().clear().apply(); - Intent intent = new Intent(context, LoginActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); + Intent intent2 = new Intent(context, LoginActivity.class); + intent2.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent2); } } \ No newline at end of file diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/LoginPresenter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/LoginPresenter.java index fa5040c58d09565f9d9380f1deb6931f2a1fd99b..14de8aec3e870320e4dc2fc5a2a803ddf38d4b90 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/LoginPresenter.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/LoginPresenter.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.graphics.BitmapFactory; import android.os.Bundle; import android.util.Log; +import android.widget.Toast; import com.firebase.jobdispatcher.Constraint; import com.firebase.jobdispatcher.FirebaseJobDispatcher; @@ -34,6 +35,7 @@ import br.ufpr.c3sl.agendador.agendador.models.AccountOutput; import br.ufpr.c3sl.agendador.agendador.network.ApiEndpoints; import br.ufpr.c3sl.agendador.agendador.network.ApiUtils; import br.ufpr.c3sl.agendador.agendador.services.ImageUpdateService; +import br.ufpr.c3sl.agendador.agendador.services.notification.NotificationsListService; import br.ufpr.c3sl.agendador.agendador.views.LoginView; import okhttp3.Headers; import okhttp3.ResponseBody; @@ -107,6 +109,8 @@ public class LoginPresenter extends BasePresenter<LoginView> { break; default: Log.d("Login Request", "Voltou " + status + "!!!"); + loginPresenter.view().setProgressBar(false); + Toast.makeText(context, "Ocorreu um erro ao logar.", Toast.LENGTH_SHORT).show(); break; } } @@ -191,25 +195,55 @@ public class LoginPresenter extends BasePresenter<LoginView> { } + + + public void createServiceLocalNotificationsList(){ + + FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher( + new GooglePlayDriver(context) + ); + + Job job = dispatcher.newJobBuilder() + .setService(NotificationsListService.class) + .setTag(Utils.NOTIFICATION_UPDATE_LIST_SERVICE) + //time the function gets is in seconds, 60s = 1 minutes, 1 hour = 3600 + //will be repeated every 10 minutes + .setTrigger(Trigger.executionWindow(570, 610)) + //will be retried if it fails. + .setRecurring(true) + .setRetryStrategy(RetryStrategy.DEFAULT_LINEAR) + .setConstraints( + // only run on network + Constraint.ON_ANY_NETWORK + ) + //if device is rebooted the job will be restarted + .setLifetime(Lifetime.FOREVER) + .build(); + + dispatcher.mustSchedule(job); + } + // TODO: 03/05/17 Adapt function to scenario with different file extensions, if needed. private void checkLocalPhoto(){ final String uid = osb.getString(Utils.UID, null); String compare, children[]; - Locale locale = new Locale("pt", "BR"); - DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", locale); - sdf.setTimeZone(TimeZone.getTimeZone("GMT")); + + DateFormat sdf = new SimpleDateFormat(Utils.SERVER_FORMAT, Locale.getDefault()); sdf.setLenient(false); Date fileDate = null; //in case of parsing exception, it will request image from server and app won't crash. Date serverDate = Calendar.getInstance().getTime(); + Log.d("Agendador", "Data sem parsear:" + osb.getString(Utils.AVATAR_UPDATED_AT, "")); try { serverDate = sdf.parse(osb.getString(Utils.AVATAR_UPDATED_AT, "")); - } catch (ParseException e) { - e.printStackTrace(); + Log.d("Agendador", "Data parseada:" + sdf.format(serverDate)); + } catch (ParseException ignored) { + //in case of parse exception request from server + ignored.printStackTrace(); } File dir = new File(context.getExternalFilesDir(null) + File.separator + Utils.DIR_PICTRS + File.separator); @@ -238,8 +272,6 @@ public class LoginPresenter extends BasePresenter<LoginView> { loginPresenter.view().setProgressBar(false); loginPresenter.view().afterSuccessfulLogin(); } - - } } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/ScheduleConfirmationPresenter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/ScheduleConfirmationPresenter.java index 759f8044e130088c1008dd55d0a512d347727dd6..0a96900b88b0658aa3ecfc4f8639a9278488082a 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/ScheduleConfirmationPresenter.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/ScheduleConfirmationPresenter.java @@ -1,7 +1,6 @@ package br.ufpr.c3sl.agendador.agendador.presenters; import android.content.Context; -import android.content.Intent; import android.util.Log; import com.firebase.jobdispatcher.FirebaseJobDispatcher; @@ -10,14 +9,13 @@ import com.firebase.jobdispatcher.GooglePlayDriver; import java.util.HashMap; import java.util.Map; -import br.ufpr.c3sl.agendador.agendador.LoginActivity; import br.ufpr.c3sl.agendador.agendador.ScheduleConfirmationActivity; import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; import br.ufpr.c3sl.agendador.agendador.helpers.Utils; import br.ufpr.c3sl.agendador.agendador.models.CitizenCompact; +import br.ufpr.c3sl.agendador.agendador.models.Notification; import br.ufpr.c3sl.agendador.agendador.models.ScheduleConfirmation; import br.ufpr.c3sl.agendador.agendador.models.ScheduleNote; -import br.ufpr.c3sl.agendador.agendador.models.UserOutput; import br.ufpr.c3sl.agendador.agendador.network.ApiEndpoints; import br.ufpr.c3sl.agendador.agendador.network.ApiUtils; import okhttp3.Headers; @@ -33,14 +31,20 @@ public class ScheduleConfirmationPresenter extends BasePresenter<ScheduleConfirm private ScheduleConfirmationPresenter scheduleConfirmationPresenter; + private FirebaseJobDispatcher dispatcher; + public ScheduleConfirmationPresenter(Context context){ this.context = context; this.scheduleConfirmationPresenter = this; osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE); + + dispatcher = new FirebaseJobDispatcher( + new GooglePlayDriver(context) + ); } - public void onScheduleClicked(final ScheduleConfirmation scheduleConfirmation, CitizenCompact citizen){ + public void onScheduleClicked(final ScheduleConfirmation scheduleConfirmation, CitizenCompact citizen, String note, Notification notification){ Map<String, String> header = new HashMap<>(); header.put("Content-Type", "application/json"); header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null)); @@ -49,9 +53,11 @@ public class ScheduleConfirmationPresenter extends BasePresenter<ScheduleConfirm final ApiEndpoints service = ApiUtils.request(header); - ScheduleNote note = new ScheduleNote(""); + ScheduleNote scheduleNote = new ScheduleNote(note); + + scheduleNote.notification = notification; - Call<ScheduleConfirmation> listCall = service.requestPerformScheduling(scheduleConfirmation.getId(), citizen.getId(), note); + Call<ScheduleConfirmation> listCall = service.requestPerformScheduling(scheduleConfirmation.getId(), citizen.getId(), scheduleNote); scheduleConfirmationPresenter.view().setProgressBar(true); listCall.enqueue(new Callback<ScheduleConfirmation>() { @@ -60,14 +66,14 @@ public class ScheduleConfirmationPresenter extends BasePresenter<ScheduleConfirm public void onResponse(Call<ScheduleConfirmation> call, Response<ScheduleConfirmation> response) { Headers headers = response.headers(); int status = response.code(); - //ScheduleConfirmation confirmationResponse = response.body(); + updateHeaders(headers); switch (status) { case 200: Log.d("Server response", this.getClass().getName() + ": 200 - Sucesso!"); scheduleConfirmationPresenter.view().setProgressBar(false); + scheduleConfirmationPresenter.view().returnHome(); - scheduleConfirmationPresenter.view().onSuccess(); break; default: Log.e("Server response", this.getClass().getName() + ": ERRO:" + status); @@ -87,6 +93,7 @@ public class ScheduleConfirmationPresenter extends BasePresenter<ScheduleConfirm } + @Override protected void updateView() {} } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/ScheduleInfoPresenter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/ScheduleInfoPresenter.java new file mode 100644 index 0000000000000000000000000000000000000000..44dddc64c391de1f8696406cf931ac2495fa06fb --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/ScheduleInfoPresenter.java @@ -0,0 +1,97 @@ +package br.ufpr.c3sl.agendador.agendador.presenters; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; + +import br.ufpr.c3sl.agendador.agendador.ScheduleInfoActivity; +import br.ufpr.c3sl.agendador.agendador.SchedulesActivity; +import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; +import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.CheckSchedules; +import br.ufpr.c3sl.agendador.agendador.models.ScheduleInfo; +import br.ufpr.c3sl.agendador.agendador.network.ApiEndpoints; +import br.ufpr.c3sl.agendador.agendador.network.ApiUtils; +import okhttp3.Headers; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * Created by Lucas Braz Cunha on 15/01/18. + */ + +public class ScheduleInfoPresenter extends BasePresenter<ScheduleInfoActivity> { + + private ScheduleInfoPresenter presenter; + + public ScheduleInfoPresenter(Context context) { + this.context = context; + presenter = this; + osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE); + } + + + + public void requestScheduleInfo(long id){ + Map<String, String> header = new HashMap<>(); + header.put("Content-Type", "application/json"); + header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null)); + header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null)); + header.put(Utils.UID, osb.getString(Utils.UID, null)); + + final ApiEndpoints service = ApiUtils.request(header); + Call<ScheduleInfo> listCall = service.requestScheduleInfo(id); + + presenter.view().setProgressBar(true); + listCall.enqueue(new Callback<ScheduleInfo>() { + + @Override + public void onResponse(Call<ScheduleInfo> call, Response<ScheduleInfo> response) { + Headers headers = response.headers(); + int status = response.code(); + ScheduleInfo scheduleInfo = response.body(); + updateHeaders(headers); + switch (status) { + case 200: + CheckSchedules.Schedule schedule = new CheckSchedules.Schedule(scheduleInfo.getId(), scheduleInfo.getStartTime(), + scheduleInfo.getSectorName(), + scheduleInfo.getServiceTypeName(), scheduleInfo.getLocationName(), + scheduleInfo.getAddressStreet(), scheduleInfo.getAddressNumber(), + scheduleInfo.getSituation()); + Log.d("Server response", getClass().getName() + ": 200 - Sucesso!"); + presenter.view().showScheduleContent(schedule,scheduleInfo.getCitizenName()); + presenter.view().setProgressBar(false); + break; + default: + Log.e("Server response", getClass().getName() + ": ERRO:" + status); + Log.v("Server response", "Redirecionando para tela de visualizar agendamentos."); + Intent i = new Intent(context, SchedulesActivity.class); + i.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + presenter.view().finish(); + context.startActivity(i); + break; + } + + } + + @Override + public void onFailure(Call<ScheduleInfo> call, Throwable t) { + Log.e("Server response", getClass().getName() + ": Requisição falhou!!"); + presenter.view().setProgressBar(false); + presenter.view().setNoConnection(true); + t.printStackTrace(); + } + }); + + } + + + @Override + protected void updateView() { + + } +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesHistoryPresenter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesHistoryPresenter.java index 42a77ce1344cbe34402312d8b3afa5a54598d649..0055f1262d299d99f3aa765263b889792a024559 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesHistoryPresenter.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesHistoryPresenter.java @@ -77,6 +77,7 @@ public class SchedulesHistoryPresenter extends BasePresenter<SchedulesHistoryAct presenter.view().setNoConnection(true); presenter.view().enableSpinners(false); presenter.view().enableButtons(false); + t.printStackTrace(); } }); @@ -124,6 +125,7 @@ public class SchedulesHistoryPresenter extends BasePresenter<SchedulesHistoryAct presenter.view().setNoConnection(true); presenter.view().setProgressBar(false); presenter.view().enableSpinners(true); + t.printStackTrace(); } }); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesPresenter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesPresenter.java index c8197a6d926bcaf6f9fa783620ab2b48a5e16c60..d17c668722512f88a6b47e1cba6ea70ce962a74e 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesPresenter.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulesPresenter.java @@ -51,7 +51,7 @@ public class SchedulesPresenter extends BasePresenter<SchedulesActivity> { // TODO: 06/10/17 Change to request "active" schedules1 - Call<CheckSchedules> listCall = service.requestSchedulesHistory(new HashMap<String, String>()); + Call<CheckSchedules> listCall = service.requestFutureSchedules(new HashMap<String, String>()); listCall.enqueue(new Callback<CheckSchedules>() { @@ -80,6 +80,7 @@ public class SchedulesPresenter extends BasePresenter<SchedulesActivity> { presenter.view().setNoConnection(true); presenter.view().setProgressBar(false); presenter.view().blockCalendar(false); + t.printStackTrace(); } }); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulingPresenter.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulingPresenter.java index 580504afa4901159853a556b7f99ecbd1d3a049d..42b34555a490ecb5d6ecfd8ae09a1c1b02aa8309 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulingPresenter.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/presenters/SchedulingPresenter.java @@ -93,6 +93,7 @@ public class SchedulingPresenter extends BasePresenter<SchedulingView> { header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null)); header.put(Utils.UID, osb.getString(Utils.UID, null)); + final ApiEndpoints service = ApiUtils.request(header); Call<ScheduleConfirmation> listCall = service.requestConfirmSchedule(schedule.getmId(), citizen.getId()); diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/BootReceiver.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/BootReceiver.java new file mode 100644 index 0000000000000000000000000000000000000000..9262670e6f0244d6b6419615a68ff302c4ecc993 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/BootReceiver.java @@ -0,0 +1,72 @@ +package br.ufpr.c3sl.agendador.agendador.services; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; +import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.Notification; +import br.ufpr.c3sl.agendador.agendador.services.notification.LocalNotificationManager; +import br.ufpr.c3sl.agendador.agendador.services.notification.NotificationReceiver; + +/** + * Created by Lucas Braz Cunha on 27/11/17. + * + * Receiver responsible for re-scheduling notifications on reboot, if it's needed. + */ + + +public class BootReceiver extends BroadcastReceiver { + + + @Override + public void onReceive(Context context, Intent intent) { + PendingResult result; + if (intent.getAction() != null && context != null && intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { + // Set the alarm here. + result = goAsync(); + Date date; + Calendar calendar; + SimpleDateFormat sdfServer = new SimpleDateFormat(Utils.SERVER_FORMAT, Locale.getDefault()); + ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE); + + //Retrieve the values + Gson gson = new Gson(); + String jsonText = osb.getString(Utils.NOTIFICATION_LIST, null); + List<Notification> notifications = gson.fromJson(jsonText, new TypeToken<List<Notification>>(){}.getType()); + + + if (notifications != null) { + Log.d("Agendador", "Tamanho da lista:" + notifications.size()); + for (Notification notification: notifications) { + calendar = Calendar.getInstance(); + try { + date = sdfServer.parse(notification.reminderTime); + calendar.setTime(date); + LocalNotificationManager.setReminder(context, calendar, (int) notification.scheduleId); + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + + + result.finish(); + } + } + + + +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/ImageUpdateService.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/ImageUpdateService.java index 660d53e6c532e6a14c367a9e4e2c1c0dbc79d380..4a7d303ec3638e718f7f40253d85dc6b2ef07aa8 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/ImageUpdateService.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/ImageUpdateService.java @@ -30,7 +30,6 @@ import retrofit2.Response; public class ImageUpdateService extends JobService { - /** * The entry point to your Job. Implementations should offload work to another thread of * execution as soon as possible. @@ -88,10 +87,10 @@ public class ImageUpdateService extends JobService { break; case 404: //user has no picture on back-end - Log.d("AGNDDR-ImgService", "Job Foi executado e usuário não possui foto"); + Log.d("AGNDDR-ImgService", "Job foi executado e usuário não possui foto"); jobFinished(job, false); default: - Log.d("AGNDDR-ImgService", "Job Foi executado mas recebeu retorno " + status); + Log.d("AGNDDR-ImgService", "Job foi executado mas recebeu retorno " + status); jobFinished(job, true); break; diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/LocalNotificationManager.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/LocalNotificationManager.java new file mode 100644 index 0000000000000000000000000000000000000000..6fdf7c1662c8703d19378a69c91816fb4326adfc --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/LocalNotificationManager.java @@ -0,0 +1,131 @@ +package br.ufpr.c3sl.agendador.agendador.services.notification; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.util.Log; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; + +import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; +import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.NotificationReturn; + +import static android.content.Context.ALARM_SERVICE; + +/** + * Utility Class to manipulate the local list of notifications. + * + * Created by Lucas Braz Cunha on 15/12/17. + */ + + +public abstract class LocalNotificationManager { + + public static final int NOTIFICATION_REMINDER_REQUEST_CODE = 0x27; + + + public synchronized static void createLocalNotification(Context context, NotificationReturn notification, ObscuredSharedPreferences osb, Gson gson){ + addNotification(notification, osb, gson); + SimpleDateFormat sdfServer = new SimpleDateFormat(Utils.SERVER_FORMAT, Locale.getDefault()); + Calendar calendar = Calendar.getInstance(); + + try{ + calendar.setTime(sdfServer.parse(notification.reminderTime)); + setReminder(context, calendar, notification.scheduleId.intValue()); + }catch (ParseException ignored){ + Log.e("Notificacao", "Erro ao parsear data, para criar notificação local."); + ignored.printStackTrace(); + } + } + + + public synchronized static void setReminder(Context context, Calendar calendar, int scheduleId){ + + Intent intent = new Intent(context, NotificationReceiver.class); + intent.setAction(Utils.ACTION_SHOW_NOTIFICATION); + intent.putExtra(Utils.ID, scheduleId); + + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, + NOTIFICATION_REMINDER_REQUEST_CODE + scheduleId, intent, + PendingIntent.FLAG_UPDATE_CURRENT); + AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE); + if(am != null){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); + }else { + am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); + } + + Log.d("Notificacao", "Alarme registrado para o agendamento " + scheduleId + " ."); + } + else{ + Log.d("Notificacao", "Objeto do Alarm Manager NULL."); + } + } + + + private synchronized static void addNotification(NotificationReturn notification, ObscuredSharedPreferences osb, Gson gson){ + + synchronized (LocalNotificationManager.class) { + //Retrieve the values + String jsonList = osb.getString(Utils.NOTIFICATION_LIST, ""); + List<NotificationReturn> notifications = gson.fromJson(jsonList, new TypeToken<List<NotificationReturn>>() { + }.getType()); + if (notifications == null) + notifications = new ArrayList<>(); + notifications.add(notification); + jsonList = gson.toJson(notifications); + + + osb.edit().putString(Utils.NOTIFICATION_LIST, jsonList).commit(); + } + } + + // TODO: 15/12/17 Testar o retorno da lista. + public synchronized static List<NotificationReturn> getList(ObscuredSharedPreferences osb, Gson gson){ + synchronized (LocalNotificationManager.class) { + String jsonList = osb.getString(Utils.NOTIFICATION_LIST, ""); + List<NotificationReturn> notifications = gson.fromJson(jsonList, new TypeToken<List<NotificationReturn>>() { + }.getType()); + return notifications == null ? new ArrayList<NotificationReturn>() : notifications; + } + } + + // TODO: 15/12/17 Testar o remove. + public synchronized static void removeNotification(NotificationReturn notification, ObscuredSharedPreferences osb, Gson gson){ + + synchronized (LocalNotificationManager.class) { + //Retrieve the values + String jsonList = osb.getString(Utils.NOTIFICATION_LIST, ""); + List<NotificationReturn> notifications = gson.fromJson(jsonList, new TypeToken<List<NotificationReturn>>() { + }.getType()); + + for (NotificationReturn n : notifications) { + if (n.notificationId == notification.notificationId) { + notifications.remove(n); + break; + } + } + + jsonList = gson.toJson(notifications); + + + osb.edit().putString(Utils.NOTIFICATION_LIST, jsonList).commit(); + } + } + + + + +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationReadService.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationReadService.java new file mode 100644 index 0000000000000000000000000000000000000000..4a43b8015a7cf6bae742ab54273fc03f1df5f1d4 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationReadService.java @@ -0,0 +1,141 @@ +package br.ufpr.c3sl.agendador.agendador.services.notification; + +import android.content.Context; +import android.util.Log; + +import com.firebase.jobdispatcher.JobParameters; +import com.firebase.jobdispatcher.JobService; +import com.google.gson.Gson; + +import java.util.HashMap; +import java.util.Map; + +import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; +import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.NotificationReturn; +import br.ufpr.c3sl.agendador.agendador.models.NotificationUpdate; +import br.ufpr.c3sl.agendador.agendador.network.ApiEndpoints; +import br.ufpr.c3sl.agendador.agendador.network.ApiUtils; +import okhttp3.Headers; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * Created by Lucas Braz Cunha on 22/01/18. + * + * + * Service to update back-end's notification, now that user has been notified. + */ + +public class NotificationReadService extends JobService { + /** + * The entry point to your Job. Implementations should offload work to another thread of + * execution as soon as possible. + * + */ + @Override + public boolean onStartJob(final JobParameters job) { + + + new Thread(new Runnable() { + public void run() { + + Log.v("Notificacao", "Rodando " + NotificationReadService.class.getName()); + Map<String, String> header = new HashMap<>(); + ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(getBaseContext(), "Agendador", Context.MODE_PRIVATE); + header.put("Content-Type", "application/json"); + header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null)); + header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null)); + header.put(Utils.UID, osb.getString(Utils.UID, null)); + requestNotificationRead(header, job, osb); + } + + }).start(); + + return true; + } + + + private void requestNotificationRead(final Map<String, String> header, final JobParameters job, final ObscuredSharedPreferences osb){ + + final ApiEndpoints service = ApiUtils.request(header); + + + if(job.getExtras() == null){ + Log.d("Notificacao", "Job 'extras' está vazio."); + jobFinished(job, false); + return; + } + + String notificationJson = job.getExtras().getString(Utils.NOTIFICATION); + final NotificationReturn notificationReturn = new Gson().fromJson(notificationJson, NotificationReturn.class); + + if(notificationReturn == null){ + Log.d("Notificacao", "Conteudo para atualizar está vazio."); + jobFinished(job, false); + return; + } + + NotificationUpdate notificationUpdate = new NotificationUpdate(1); + + Call<NotificationReturn> listCall = service.requestUpdateNotification(notificationReturn.notificationId, notificationUpdate); + + listCall.enqueue(new Callback<NotificationReturn>() { + + @Override + public void onResponse(Call<NotificationReturn> call, Response<NotificationReturn> response) { + Headers headers = response.headers(); + int status = response.code(); + updateHeaders(headers, osb); + switch (status) { + case 200: + case 201: + Log.d("Server response", getClass().getName() + ": 200 - Sucesso!"); + LocalNotificationManager.removeNotification(notificationReturn, osb, new Gson()); + jobFinished(job, false); + break; + default: + Log.e("Server response", getClass().getName() + ": ERRO:" + status); + + jobFinished(job, true); + break; + } + + } + + @Override + public void onFailure(Call<NotificationReturn> call, Throwable t) { + Log.e("Server response", getClass().getName() + ": Requisição falhou!!"); + t.printStackTrace(); + jobFinished(job, true); + } + }); + + } + + private void updateHeaders(Headers headers, ObscuredSharedPreferences osb){ + if (headers.get(Utils.ACCESS_TOKEN) != null) + osb.edit().putString(Utils.ACCESS_TOKEN, headers.get(Utils.ACCESS_TOKEN)).apply(); + if (headers.get(Utils.CLIENT) != null) + osb.edit().putString(Utils.CLIENT, headers.get(Utils.CLIENT)).apply(); + if (headers.get(Utils.UID) != null) + osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply(); + if (headers.get(Utils.EXPIRY) != null) + osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply(); + + } + + + + /** + * Called when the scheduling engine has decided to interrupt the execution of a running job, + * most likely because the runtime constraints associated with the job are no longer satisfied. + * The job must stop execution. + * + */ + @Override + public boolean onStopJob(JobParameters job) { + return false; + } +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationReceiver.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationReceiver.java new file mode 100644 index 0000000000000000000000000000000000000000..a84d08f728511ffebbffac688841d8ca8a671df0 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationReceiver.java @@ -0,0 +1,185 @@ +package br.ufpr.c3sl.agendador.agendador.services.notification; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.support.v7.app.NotificationCompat; +import android.util.Log; +import android.widget.Toast; + +import com.firebase.jobdispatcher.Constraint; +import com.firebase.jobdispatcher.FirebaseJobDispatcher; +import com.firebase.jobdispatcher.GooglePlayDriver; +import com.firebase.jobdispatcher.Job; +import com.firebase.jobdispatcher.Lifetime; +import com.firebase.jobdispatcher.RetryStrategy; +import com.firebase.jobdispatcher.Trigger; +import com.google.gson.Gson; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import br.ufpr.c3sl.agendador.agendador.R; +import br.ufpr.c3sl.agendador.agendador.ScheduleInfoActivity; +import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; +import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.Notification; +import br.ufpr.c3sl.agendador.agendador.models.NotificationReturn; +import br.ufpr.c3sl.agendador.agendador.models.NotificationUpdate; +import br.ufpr.c3sl.agendador.agendador.network.ApiEndpoints; +import br.ufpr.c3sl.agendador.agendador.network.ApiUtils; +import okhttp3.Headers; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +import static android.content.Context.NOTIFICATION_SERVICE; + +/** + * Created by Lucas Braz Cunha on 27/11/17. + */ + +public class NotificationReceiver extends BroadcastReceiver{ + + + @Override + public void onReceive(Context context, Intent intent) { + if(intent.getAction() != null && intent.getAction().equals(Utils.ACTION_SHOW_NOTIFICATION)){ + int id = intent.getIntExtra(Utils.ID, -1); + Log.d("Notificacao", "Estou no "+ NotificationReceiver.class.getName() +"! Notificação de id "+ id); + ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE); + NotificationReturn content = null; + Gson gson = new Gson(); + List<NotificationReturn> notifications = LocalNotificationManager.getList(osb, gson); + for(NotificationReturn n : notifications){ + if(n.scheduleId == id){ + content = n; + break; + } + } + + + NotificationManager notificationManager = (NotificationManager) + context.getSystemService(NOTIFICATION_SERVICE); + + if(content == null){ + Log.e("Notificacao", "Erro: notificação não encontrada"); + }else if(notificationManager == null){ + Log.e("Notificacao", "Erro: notification manager null"); + }else{ + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context); + + Intent showScheduleIntent = new Intent(context, ScheduleInfoActivity.class); + showScheduleIntent.putExtra(Utils.ID, id); + + PendingIntent pendingIntent = PendingIntent.getActivity(context, Utils.SHOW_SCHEDULE_INFO_ACTION, showScheduleIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + String alert = context.getResources().getString(R.string.notification_alert); + android.app.Notification appNotification = mBuilder.setAutoCancel(true).setContentText(content.description).setContentTitle(alert) + .setDefaults(android.app.Notification.DEFAULT_LIGHTS | android.app.Notification.DEFAULT_SOUND) + .setSmallIcon(R.drawable.img_mainact_logo).setTicker(alert).setContentIntent(pendingIntent) + .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.img_terms_logo)).build(); + + + + requestNotificationUpdate(osb, content, context); + + // Will display the notification in the notification bar + notificationManager.notify(id, appNotification); + + } + } + } + + + + private void requestNotificationUpdate(final ObscuredSharedPreferences osb, final NotificationReturn notificationReturn, final Context context){ + Map<String, String> header = new HashMap<>(); + header.put("Content-Type", "application/json"); + header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null)); + header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null)); + header.put(Utils.UID, osb.getString(Utils.UID, null)); + + final ApiEndpoints service = ApiUtils.request(header); + + + NotificationUpdate notificationUpdate = new NotificationUpdate(1); + + Call<NotificationReturn> listCall = service.requestUpdateNotification(notificationReturn.notificationId, notificationUpdate); + + listCall.enqueue(new Callback<NotificationReturn>() { + + @Override + public void onResponse(Call<NotificationReturn> call, Response<NotificationReturn> response) { + Headers headers = response.headers(); + int status = response.code(); + updateHeaders(headers, osb); + switch (status) { + case 200: + Log.d("Server response", getClass().getName() + ": 200 - Sucesso!"); + LocalNotificationManager.removeNotification(notificationReturn, osb, new Gson()); + break; + default: + Log.e("Server response", getClass().getName() + ": ERRO:" + status); + scheduleJob(context, notificationReturn); + break; + } + + } + + @Override + public void onFailure(Call<NotificationReturn> call, Throwable t) { + Log.e("Server response", getClass().getName() + ": Requisição falhou!!"); + t.printStackTrace(); + } + }); + + } + + private void updateHeaders(Headers headers, ObscuredSharedPreferences osb){ + if (headers.get(Utils.ACCESS_TOKEN) != null) + osb.edit().putString(Utils.ACCESS_TOKEN, headers.get(Utils.ACCESS_TOKEN)).apply(); + if (headers.get(Utils.CLIENT) != null) + osb.edit().putString(Utils.CLIENT, headers.get(Utils.CLIENT)).apply(); + if (headers.get(Utils.UID) != null) + osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply(); + if (headers.get(Utils.EXPIRY) != null) + osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply(); + + } + + + private void scheduleJob(Context context, NotificationReturn notificationReturn){ + + FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher( + new GooglePlayDriver(context) + ); + + Bundle extras = new Bundle(); + + extras.putString(Utils.NOTIFICATION, new Gson().toJson(notificationReturn)); + + Job job = dispatcher.newJobBuilder() + .setService(NotificationReadService.class) + .setTag(Utils.NOTIFICATION_READ_SERVICE) + //time the function gets is in seconds, 60s = 1 minutes, 1 hour = 3600 + //will be repeated every 10 minutes + .setTrigger(Trigger.NOW) + //will be retried if it fails. + .setRetryStrategy(RetryStrategy.DEFAULT_LINEAR) + .setExtras(extras) + .setConstraints( + // only run on network + Constraint.ON_ANY_NETWORK + ) + //if device is rebooted the job will be restarted + .setLifetime(Lifetime.FOREVER) + .build(); + dispatcher.mustSchedule(job); + } +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationsListService.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationsListService.java new file mode 100644 index 0000000000000000000000000000000000000000..830028826aca932c72cb19d6fa8926894ab41945 --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/services/notification/NotificationsListService.java @@ -0,0 +1,148 @@ +package br.ufpr.c3sl.agendador.agendador.services.notification; + +import android.content.Context; +import android.util.Log; +import android.util.LongSparseArray; + +import com.firebase.jobdispatcher.JobParameters; +import com.firebase.jobdispatcher.JobService; +import com.google.gson.Gson; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences; +import br.ufpr.c3sl.agendador.agendador.helpers.Utils; +import br.ufpr.c3sl.agendador.agendador.models.Notification; +import br.ufpr.c3sl.agendador.agendador.models.NotificationReturn; +import br.ufpr.c3sl.agendador.agendador.network.ApiEndpoints; +import br.ufpr.c3sl.agendador.agendador.network.ApiUtils; +import okhttp3.Headers; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * Created by Lucas B. Cunha on 17/01/18. + * + * Service to keep local list of notifications synchronized. + */ + +public class NotificationsListService extends JobService { + /** + * The entry point to your Job. Implementations should offload work to another thread of + * execution as soon as possible. + * + * @param job + * @return whether there is more work remaining. + */ + @Override + public boolean onStartJob(final JobParameters job) { + + + + new Thread(new Runnable() { + public void run() { + + Log.v("Notificacao", "Rodando " + NotificationsListService.class.getName()); + Map<String, String> header = new HashMap<>(); + ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(getBaseContext(), "Agendador", Context.MODE_PRIVATE); + header.put("Content-Type", "application/json"); + header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null)); + header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null)); + header.put(Utils.UID, osb.getString(Utils.UID, null)); + requestNotificationUpdate(header, job, osb); + } + + }).start(); + + return true; + } + + + private void requestNotificationUpdate(final Map<String, String> header, final JobParameters job, final ObscuredSharedPreferences osb){ + + final ApiEndpoints service = ApiUtils.request(header); + + Call<List<NotificationReturn>> listCall = service.requestNotifications(); + + listCall.enqueue(new Callback<List<NotificationReturn>>() { + + @Override + public void onResponse(Call<List<NotificationReturn>> call, Response<List<NotificationReturn>> response) { + Headers headers = response.headers(); + int status = response.code(); + updateHeaders(headers, osb); + final List<NotificationReturn> responseList = response.body(); + switch (status) { + case 200: + case 201: + new Thread(new Runnable() { + public void run() { + Gson gson = new Gson(); + LongSparseArray<NotificationReturn> newLocalNotifications = new LongSparseArray<>(); + List<NotificationReturn> list = LocalNotificationManager.getList(osb, gson); + Log.d("Notificacao", "NOTIFICACOES LOCAIS!!!!!!!!!"); + for(NotificationReturn n : list){ + newLocalNotifications.append(n.notificationId, n); + Log.d("Notificacao", gson.toJson(n)); + } + Log.d("Notificacao", "NOTIFICACOES NO SERVIDOR"); + for(NotificationReturn nr : responseList){ + Log.d("Notificacao", gson.toJson(nr)); + if(newLocalNotifications.get(nr.notificationId) == null && nr.hasRead){ + LocalNotificationManager.createLocalNotification(getApplicationContext(), + nr, osb, gson); + } + } + + + jobFinished(job, false); + }}).start(); + break; + default: + Log.e("Server response", getClass().getName() + ": ERRO:" + status); + + jobFinished(job, false); + break; + } + + } + + @Override + public void onFailure(Call<List<NotificationReturn>> call, Throwable t) { + Log.e("Server response", getClass().getName() + ": Requisição falhou!!"); + jobFinished(job, false); + } + }); + + } + + private void updateHeaders(Headers headers, ObscuredSharedPreferences osb){ + if (headers.get(Utils.ACCESS_TOKEN) != null) + osb.edit().putString(Utils.ACCESS_TOKEN, headers.get(Utils.ACCESS_TOKEN)).apply(); + if (headers.get(Utils.CLIENT) != null) + osb.edit().putString(Utils.CLIENT, headers.get(Utils.CLIENT)).apply(); + if (headers.get(Utils.UID) != null) + osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply(); + if (headers.get(Utils.EXPIRY) != null) + osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply(); + + } + + + /** + * Called when the scheduling engine has decided to interrupt the execution of a running job, + * most likely because the runtime constraints associated with the job are no longer satisfied. + * The job must stop execution. + * + * @param job + * @return true if the job should be retried + * + */ + @Override + public boolean onStopJob(JobParameters job) { + return true; + } +} diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/views/ScheduleConfirmationView.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/views/ScheduleConfirmationView.java index 930089ba4d1b87c89b9c57199afd003871cb03db..0c41bbaa447174ef06ed3f8072b9f69c9e7a775a 100644 --- a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/views/ScheduleConfirmationView.java +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/views/ScheduleConfirmationView.java @@ -10,8 +10,8 @@ public interface ScheduleConfirmationView { void setProgressBar(boolean enabled); - void onSuccess(); - void setConnectionError(boolean enabled); + void returnHome(); + } diff --git a/app/src/main/java/br/ufpr/c3sl/agendador/agendador/views/ScheduleInfoView.java b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/views/ScheduleInfoView.java new file mode 100644 index 0000000000000000000000000000000000000000..eaee154e225c4085963f4b28307f5d09f3eb1c1a --- /dev/null +++ b/app/src/main/java/br/ufpr/c3sl/agendador/agendador/views/ScheduleInfoView.java @@ -0,0 +1,18 @@ +package br.ufpr.c3sl.agendador.agendador.views; + +import br.ufpr.c3sl.agendador.agendador.models.CheckSchedules; + +/** + * Created by Lucas B. Cunha on 15/01/18. + */ + +public interface ScheduleInfoView { + + void setProgressBar(boolean enabled); + + void setConnectionError(boolean enabled); + + void setNoConnection(boolean enabled); + + void showScheduleContent(CheckSchedules.Schedule schedule, String citizenName); +} diff --git a/app/src/main/res/layout/content_schedule_confirmation.xml b/app/src/main/res/layout/content_schedule_confirmation.xml index 04ccbdb0fcb1d5a501929536457c982a7e4384bf..28553d03bb1678efd59b23b4d7925232a109fa05 100644 --- a/app/src/main/res/layout/content_schedule_confirmation.xml +++ b/app/src/main/res/layout/content_schedule_confirmation.xml @@ -20,222 +20,352 @@ android:typeface="normal" /> - <LinearLayout + <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/tv_scheduling_welcome" - android:orientation="vertical" android:background="@color/colorWhite"> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="24dp" - android:layout_marginTop="24dp" - android:layout_marginBottom="5dp" - android:gravity="center" - android:text="@string/schedule_confirmation_description" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="16sp" - android:textStyle="normal|bold" - android:typeface="normal" /> - - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="14dp" - android:layout_gravity="start" - android:text="@string/schedule_confirmation_title" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="16sp" - android:textStyle="normal|bold" - android:typeface="normal"/> - - <RelativeLayout + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp"> + android:focusableInTouchMode="true" + android:orientation="vertical"> <TextView - android:id="@+id/tv_schedule_citizen" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="14dp" - android:layout_gravity="start" + android:layout_marginStart="24dp" + android:layout_marginTop="24dp" + android:layout_marginBottom="5dp" + android:gravity="center" + android:text="@string/schedule_confirmation_description" android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:text="@string/schedule_confirmation_citizen" android:textSize="16sp" android:textStyle="normal|bold" - android:typeface="normal"/> + android:typeface="normal" /> - <TextView - android:id="@+id/tv_schedule_citizen_content" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="14sp" - android:typeface="normal" - android:layout_marginStart="3dp" - android:layout_alignBaseline="@+id/tv_schedule_citizen" - android:layout_alignBottom="@+id/tv_schedule_citizen" - android:layout_toEndOf="@+id/tv_schedule_citizen" /> <TextView - android:id="@+id/tv_schedule_sector" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginStart="14dp" android:layout_gravity="start" - android:text="@string/schedule_confirmation_sector" + android:text="@string/schedule_confirmation_title" android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textSize="16sp" android:textStyle="normal|bold" - android:typeface="normal" - android:layout_below="@+id/tv_schedule_citizen_content" - android:layout_marginTop="20dp" - android:layout_alignStart="@+id/tv_schedule_citizen" /> + android:typeface="normal"/> - <TextView - android:id="@+id/tv_schedule_sector_content" - android:layout_width="wrap_content" + <RelativeLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="14sp" - android:typeface="normal" - android:layout_marginStart="3dp" - android:layout_alignBaseline="@+id/tv_schedule_sector" - android:layout_alignBottom="@+id/tv_schedule_sector" - android:layout_toEndOf="@+id/tv_schedule_sector"/> + android:layout_marginTop="10dp"> + + <TextView + android:id="@+id/tv_schedule_citizen" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="14dp" + android:layout_gravity="start" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:text="@string/schedule_confirmation_citizen" + android:textSize="16sp" + android:textStyle="normal|bold" + android:typeface="normal"/> + + <TextView + android:id="@+id/tv_schedule_citizen_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="14sp" + android:typeface="normal" + android:layout_marginStart="3dp" + android:layout_alignBaseline="@+id/tv_schedule_citizen" + android:layout_alignBottom="@+id/tv_schedule_citizen" + android:layout_toEndOf="@+id/tv_schedule_citizen" /> + + <TextView + android:id="@+id/tv_schedule_sector" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="start" + android:text="@string/schedule_confirmation_sector" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="16sp" + android:textStyle="normal|bold" + android:typeface="normal" + android:layout_below="@+id/tv_schedule_citizen_content" + android:layout_marginTop="20dp" + android:layout_alignStart="@+id/tv_schedule_citizen" /> + + <TextView + android:id="@+id/tv_schedule_sector_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="14sp" + android:typeface="normal" + android:layout_marginStart="3dp" + android:layout_alignBaseline="@+id/tv_schedule_sector" + android:layout_alignBottom="@+id/tv_schedule_sector" + android:layout_toEndOf="@+id/tv_schedule_sector"/> + + <TextView + android:id="@+id/tv_schedule_location" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/schedule_confirmation_place" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="16sp" + android:textStyle="normal|bold" + android:typeface="normal" + android:layout_marginTop="15dp" + android:layout_below="@id/tv_schedule_sector_content" + android:layout_alignStart="@+id/tv_schedule_citizen" /> + + <TextView + android:id="@+id/tv_schedule_location_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="14sp" + android:typeface="normal" + android:layout_marginStart="3dp" + android:layout_alignBaseline="@+id/tv_schedule_location" + android:layout_alignBottom="@+id/tv_schedule_location" + android:layout_toEndOf="@+id/tv_schedule_location"/> + + <TextView + android:id="@+id/tv_schedule_type" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/schedule_confirmation_type" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="16sp" + android:textStyle="normal|bold" + android:typeface="normal" + android:layout_marginTop="15dp" + android:layout_below="@id/tv_schedule_location_content" + android:layout_alignStart="@+id/tv_schedule_citizen" /> + + <TextView + android:id="@+id/tv_schedule_type_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="14sp" + android:typeface="normal" + android:layout_marginStart="3dp" + android:layout_alignBaseline="@+id/tv_schedule_type" + android:layout_alignBottom="@+id/tv_schedule_type" + android:layout_toEndOf="@+id/tv_schedule_type"/> + + <TextView + android:id="@+id/tv_schedule_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/schedule_confirmation_date" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="16sp" + android:textStyle="normal|bold" + android:typeface="normal" + android:layout_marginTop="15dp" + android:layout_below="@id/tv_schedule_type_content" + android:layout_alignStart="@+id/tv_schedule_citizen"/> + + <TextView + android:id="@+id/tv_schedule_date_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="14sp" + android:typeface="normal" + android:layout_marginStart="3dp" + android:layout_alignBaseline="@+id/tv_schedule_date" + android:layout_alignBottom="@+id/tv_schedule_date" + android:layout_toEndOf="@+id/tv_schedule_date"/> + + <TextView + android:id="@+id/tv_schedule_street" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/schedule_confirmation_street" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="16sp" + android:textStyle="normal|bold" + android:typeface="normal" + android:layout_marginTop="15dp" + android:layout_below="@id/tv_schedule_date_content" + android:layout_alignStart="@+id/tv_schedule_citizen"/> + + <TextView + android:id="@+id/tv_schedule_street_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + android:textSize="14sp" + android:typeface="normal" + android:layout_marginStart="3dp" + android:layout_alignBaseline="@+id/tv_schedule_street" + android:layout_alignBottom="@+id/tv_schedule_street" + android:layout_toEndOf="@+id/tv_schedule_street"/> + + </RelativeLayout> - <TextView - android:id="@+id/tv_schedule_location" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/schedule_confirmation_place" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="16sp" - android:textStyle="normal|bold" - android:typeface="normal" - android:layout_marginTop="15dp" - android:layout_below="@id/tv_schedule_sector_content" - android:layout_alignStart="@+id/tv_schedule_citizen" /> <TextView - android:id="@+id/tv_schedule_location_content" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:layout_marginStart="14dp" android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textSize="14sp" android:typeface="normal" - android:layout_marginStart="3dp" - android:layout_alignBaseline="@+id/tv_schedule_location" - android:layout_alignBottom="@+id/tv_schedule_location" - android:layout_toEndOf="@+id/tv_schedule_location"/> + android:text="@string/notification_note_text"/> - <TextView - android:id="@+id/tv_schedule_type" - android:layout_width="wrap_content" + <EditText + android:id="@+id/et_schedule_note" android:layout_height="wrap_content" - android:text="@string/schedule_confirmation_type" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="16sp" - android:textStyle="normal|bold" - android:typeface="normal" - android:layout_marginTop="15dp" - android:layout_below="@id/tv_schedule_location_content" - android:layout_alignStart="@+id/tv_schedule_citizen" /> + android:layout_width="match_parent" + android:layout_marginTop="5dp" + android:layout_marginStart="14dp" + android:layout_marginEnd="14dp" + android:textStyle="normal" + android:inputType="textAutoCorrect" + android:maxLength="140"/> + - <TextView - android:id="@+id/tv_schedule_type_content" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="14sp" - android:typeface="normal" - android:layout_marginStart="3dp" - android:layout_alignBaseline="@+id/tv_schedule_type" - android:layout_alignBottom="@+id/tv_schedule_type" - android:layout_toEndOf="@+id/tv_schedule_type"/> <TextView - android:id="@+id/tv_schedule_date" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/schedule_confirmation_date" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="16sp" - android:textStyle="normal|bold" - android:typeface="normal" android:layout_marginTop="15dp" - android:layout_below="@id/tv_schedule_type_content" - android:layout_alignStart="@+id/tv_schedule_citizen"/> + android:layout_marginStart="14dp" + android:textColor="@color/colorBlack" + android:textStyle="normal|bold" + android:text="@string/notification_time"/> - <TextView - android:id="@+id/tv_schedule_date_content" - android:layout_width="wrap_content" + + <Spinner + android:id="@+id/spnr_schedule_time" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="14sp" - android:typeface="normal" + android:layout_marginTop="2dp" android:layout_marginStart="3dp" - android:layout_alignBaseline="@+id/tv_schedule_date" - android:layout_alignBottom="@+id/tv_schedule_date" - android:layout_toEndOf="@+id/tv_schedule_date"/> + android:layout_marginEnd="3dp"/> + <TextView - android:id="@+id/tv_schedule_street" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/schedule_confirmation_street" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="16sp" - android:textStyle="normal|bold" - android:typeface="normal" android:layout_marginTop="15dp" - android:layout_below="@id/tv_schedule_date_content" - android:layout_alignStart="@+id/tv_schedule_citizen"/> + android:layout_marginStart="14dp" + android:textColor="@color/colorBlack" + android:textStyle="normal|bold" + android:text="@string/notification_email_question"/> - <TextView - android:id="@+id/tv_schedule_street_content" + <RadioGroup android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:textSize="14sp" - android:typeface="normal" - android:layout_marginStart="3dp" - android:layout_alignBaseline="@+id/tv_schedule_street" - android:layout_alignBottom="@+id/tv_schedule_street" - android:layout_toEndOf="@+id/tv_schedule_street"/> + android:layout_marginStart="14dp" + android:layout_height="wrap_content"> - </RelativeLayout> + <RadioButton + android:id="@+id/rb_notify_email_yes" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/yes"/> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:layout_marginTop="50dp"> + <RadioButton + android:id="@+id/rb_notify_email_no" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/no"/> - <Button - android:id="@+id/btn_schedule_back" - android:layout_width="wrap_content" + </RadioGroup> + + + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/gray_container_shape" - android:text="@string/back" - /> + android:layout_marginTop="10dp"> - <Button - android:id="@+id/btn_schedule_confirm" - android:layout_width="wrap_content" + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="center"> + + <TextView + android:id="@+id/tv_schedule_email" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="14dp" + android:layout_marginEnd="14dp" + android:text="@string/notification_email_dest" + android:textColor="@color/colorBlack" + android:textStyle="normal|bold" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tv_schedule_email_warning" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_marginStart="5dp" + android:text="@string/invalid_email" + android:textColor="@color/colorRed" + android:textSize="12sp" + android:visibility="invisible" /> + + </FrameLayout> + + <EditText + android:id="@+id/et_schedule_email" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="14dp" + android:layout_marginEnd="14dp" + android:layout_marginTop="5dp" + android:maxLines="1" + android:textColor="@color/colorBlack" + android:textStyle="normal" + android:inputType="textEmailAddress"/> + + + </LinearLayout> + + + + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/green_button_shape" - android:text="@string/schedule" - android:layout_marginStart="20dp" - /> + android:gravity="center" + android:layout_marginTop="30dp" + android:layout_marginBottom="30dp"> - </LinearLayout> + <Button + android:id="@+id/btn_schedule_back" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/gray_container_shape" + android:text="@string/back" + /> + <Button + android:id="@+id/btn_schedule_confirm" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/green_button_shape" + android:text="@string/schedule" + android:layout_marginStart="20dp" + /> + + </LinearLayout> + + </LinearLayout> + </ScrollView> - </LinearLayout> <ProgressBar android:id="@+id/pb_schedule_confirmation" diff --git a/app/src/main/res/layout/content_schedule_info.xml b/app/src/main/res/layout/content_schedule_info.xml index 2d07ab08b893e79e51b69f5da8409a5c8feddd6d..d459fc104be5d01715713b4c76adef79bdbbefe1 100644 --- a/app/src/main/res/layout/content_schedule_info.xml +++ b/app/src/main/res/layout/content_schedule_info.xml @@ -242,4 +242,13 @@ </LinearLayout> + <ProgressBar + android:layout_width="80dp" + android:id="@+id/pb_schedule_info" + android:layout_height="80dp" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:indeterminate="true" + android:visibility="invisible" /> + </RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/values/integer.xml b/app/src/main/res/values/integer.xml index d0c1abf8782237c7cf7aa6490c3893d74ba1d2c4..ffee45fcf60861e5063d0b7463a422f6ec6ba7ec 100644 --- a/app/src/main/res/values/integer.xml +++ b/app/src/main/res/values/integer.xml @@ -3,4 +3,17 @@ <integer name="max_length_name">150</integer> + <integer-array name="notification_time_milis"> + <item>900000</item> + <item>1800000</item> + <item>2700000</item> + <item>3600000</item> + <item>7200000</item> + <item>10800000</item> + <item>240000</item> + <item>14400000</item> + <item>86400000</item> + <item>0</item> + </integer-array> + </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 383c0dc8c5c94b37ec44ce0a54390ed1bc761322..4940c741bd9fe19c41a18e95ce4189e49562cc1e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -203,5 +203,28 @@ <string name="menu_sign_out">Sair</string> <string name="menu_edit">Editar</string> <string name="menu_sync">Sincronizar</string> + <string name="notification_email_question">Notificar também por e-mail?</string> + <string name="notification_time">Notificar sobre o agendamento:</string> + <string name="notification_email_dest">Digite o e-mail:</string> + + <string-array name="notification_time"> + <item>15 minutos antes</item> + <item>30 minutos antes</item> + <item>45 minutos antes</item> + <item>1 hora antes</item> + <item>2 horas antes</item> + <item>3 horas antes</item> + <item>4 horas antes</item> + <item>5 horas antes</item> + <item>1 dia antes</item> + <item>Não notificar</item> + </string-array> + + <string name="not_valid_email">Digite um e-mail válido.</string> + + <string name="notification_content">%s às %s, %s em %s (%s, %d)</string> + <string name="notification_note_text">Observações (Máximo de 140 caracteres):</string> + + <string name="notification_alert">Atendimento está próximo</string> </resources>