diff --git a/README.md b/README.md index 4220c34..3081800 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,12 @@ ### Результат | Главный экран | Детали контакта | | ------ | ------ | -| ![Детали](https://i.imgur.com/AavdvqJ.jpg) |![Главный экран](https://i.imgur.com/rhFKsrB.jpg) +| ![Список](https://i.imgur.com/uVBhVfT.jpg) |![Главный экран](https://i.imgur.com/rhFKsrB.jpg) ## Задание №3. Сервисы Необходимо написать привязанный сервис, который будет иметь публичное API предоставляющее список контактов. Список контактов пока захардкодить в константу Сервис должен предоставлять список контактов на другом потоке программы - т.е. должен создавать поток для загрузки списка контактов -#### Сценарий использования: +### Сценарий использования: - Главная активность привязывается к сервису с помощью bindService - Получает публичный интерфейс сервиса - Предоставляет публичный интерфейс сервиса фрагментам @@ -55,11 +55,11 @@ ## Задание №5. Content - Использование поставщика контактов -## Задание +### Задание Необходимо реализовать загрузку контактов пользователя из поставщика контактов В существующем методе загрузки списка контактов - получать список всех контактов пользователя из поставщика контактов В существующем методе загрузки деталей контакта по ID - получать все необходимые данные контакта из поставщика контактов по ID -## Проделанная работа. +### Проделанная работа. Добавлен класс-репозиторий контактов ContactRepositoryFromSystem, который позволяет получать список всех контактов, список телефонов и адресов эл.почты и подробную информацию о контакте. @@ -77,3 +77,17 @@ Пришлось отказаться от разделения на Фамилию Имя и Отчество. Вместо этого использовать объединенное поле `String displayName;` Помимо этого переделал фейковый репозиторий контактов `ContactRepositoryFakeImp` для работы с новой моделью контакта. + +## Задание №6. Рефакторинг проекта по паттерну MVP + +### Задание + +Необходимо произвести рефакторинг существующего кода приложения с использованием на выбор: 1. MVVM - использовать компоненты от Google: ViewModel + LiveData 2. MVP - в качестве библиотеки использовать Moxy В рамках рефакторинга необходимо будет удалить существующий сервис загрузки контактов, и всю работу по загрузке контактов из сервиса вынести в репозиторий, который будет использоваться в ViewModel/Presenter соответственно выбранного паттерна проектирования. + +### Проделанная работа. + +Произвел рефакторинг и изменение архитектуры на MVP с использованием библиотеки Moxy. + +Удалил существующий сервис загрузки контактов + +Всю работу по загрузке контактов перенс в репозиторий \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 7a8bfa5..207fa95 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,11 +25,16 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.legacy:legacy-support-v4:1.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + + implementation "com.arello-mobile:moxy:1.4.5" + annotationProcessor "com.arello-mobile:moxy-compiler:1.4.5" + implementation ('tech.schoolhelper:moxy-x-androidx:1.7.0'){ + exclude group: 'tech.schoolhelper', module:'moxy-x' + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f40dc36..87a4ac4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,11 +11,6 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> - - contactList); +} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/callbacks/PersonListCallback.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/callbacks/PersonListCallback.java new file mode 100644 index 0000000..9619c1c --- /dev/null +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/callbacks/PersonListCallback.java @@ -0,0 +1,9 @@ +package com.a65apps.yuhnin.lesson1.callbacks; + +import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; + +import java.util.List; + +public interface PersonListCallback { + void getPersonList(List personList); +} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/presenters/ContactDetailsPresenter.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/presenters/ContactDetailsPresenter.java new file mode 100644 index 0000000..e8eb63a --- /dev/null +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/presenters/ContactDetailsPresenter.java @@ -0,0 +1,58 @@ +package com.a65apps.yuhnin.lesson1.presenters; + +import android.os.Handler; +import android.os.Looper; + +import androidx.annotation.NonNull; + +import com.a65apps.yuhnin.lesson1.callbacks.PersonDetailsCallback; +import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; +import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; +import com.a65apps.yuhnin.lesson1.repository.ContactRepository; +import com.a65apps.yuhnin.lesson1.views.ContactDetailsView; +import com.arellomobile.mvp.InjectViewState; +import com.arellomobile.mvp.MvpPresenter; + +import java.util.List; + +@InjectViewState +public class ContactDetailsPresenter extends MvpPresenter implements PersonDetailsCallback { + @NonNull + private final ContactRepository contactRepository; + @NonNull + private final Handler handler; + + public ContactDetailsPresenter(@NonNull ContactRepository contactRepository) { + this.contactRepository = contactRepository; + this.handler = new Handler(Looper.getMainLooper()); + } + + public void requestContactsByPerson(String personId) { + contactRepository.getContactByPerson(this, personId); + } + + public void requestPersonDetails(String personId) { + contactRepository.getPersonById(this, personId); + } + + @Override + public void onFetchPersonDetails(final PersonModelAdvanced personModel) { + handler.post(new Runnable() { + @Override + public void run() { + getViewState().getContactDetails(personModel); + } + }); + } + + @Override + public void onFetchPersonContacts(final List contactList) { + handler.post(new Runnable() { + @Override + public void run() { + getViewState().getContactsInfo(contactList); + } + }); + } + +} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/presenters/ContactListPresenter.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/presenters/ContactListPresenter.java new file mode 100644 index 0000000..b911488 --- /dev/null +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/presenters/ContactListPresenter.java @@ -0,0 +1,42 @@ +package com.a65apps.yuhnin.lesson1.presenters; + +import android.os.Handler; +import android.os.Looper; + +import androidx.annotation.NonNull; + +import com.a65apps.yuhnin.lesson1.callbacks.PersonListCallback; +import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; +import com.a65apps.yuhnin.lesson1.repository.ContactRepository; +import com.a65apps.yuhnin.lesson1.views.ContactListView; +import com.arellomobile.mvp.InjectViewState; +import com.arellomobile.mvp.MvpPresenter; + +import java.util.List; + +@InjectViewState +public class ContactListPresenter extends MvpPresenter implements PersonListCallback { + @NonNull + private final ContactRepository contactRepository; + @NonNull + private final Handler handler; + + public ContactListPresenter(@NonNull ContactRepository contactRepository) { + this.contactRepository = contactRepository; + handler = new Handler(Looper.getMainLooper()); + } + + public void requestContactList() { + contactRepository.getAllPersons(this); + } + + @Override + public void getPersonList(final List personList) { + handler.post(new Runnable() { + @Override + public void run() { + getViewState().getContactList(personList); + } + }); + } +} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepository.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepository.java index f509bf8..213d6c3 100644 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepository.java +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepository.java @@ -3,19 +3,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; -import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; -import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; +import com.a65apps.yuhnin.lesson1.callbacks.PersonDetailsCallback; +import com.a65apps.yuhnin.lesson1.callbacks.PersonListCallback; import java.util.List; public interface ContactRepository { - @Nullable - List getAllPersons(); + void getAllPersons(PersonListCallback callback); - @NonNull - List getContactByPerson(String id); + void getContactByPerson(PersonDetailsCallback callback, String id); - @Nullable - PersonModelAdvanced getPersonById(String id); + void getPersonById(PersonDetailsCallback callback, String id); } diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFakeImp.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFakeImp.java index 6c569f0..a545212 100644 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFakeImp.java +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFakeImp.java @@ -3,15 +3,16 @@ import android.content.ContentResolver; import android.content.Context; import android.net.Uri; -import android.util.Log; import com.a65apps.yuhnin.lesson1.R; +import com.a65apps.yuhnin.lesson1.callbacks.PersonDetailsCallback; +import com.a65apps.yuhnin.lesson1.callbacks.PersonListCallback; import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; import com.a65apps.yuhnin.lesson1.pojo.ContactType; import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; -import java.text.ParseException; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -41,16 +42,11 @@ private void setContext(Context context) { public ContactRepositoryFakeImp() { - try { - createPersons(); - createContacts(); - }catch (ParseException ex) { - Log.e(LOG_TAG, "Ошибка формата даты рождения при создании списка контактов. " + - ex.getMessage()); - } + createPersons(); + createContacts(); } - private void createPersons() throws ParseException{ + private void createPersons() { personModelAdvanceds.add(new PersonModelAdvanced( "1", "Гагарин Юрий Алексеевич", @@ -98,30 +94,57 @@ private void createContacts() { } @Override - public List getAllPersons() { - return personModelCompacts; + public void getAllPersons(PersonListCallback callback) { + final WeakReference weakReference = new WeakReference(callback); + new Thread(new Runnable() { + @Override + public void run() { + PersonListCallback local = weakReference.get(); + if (local != null) { + local.getPersonList(personModelCompacts); + } + } + }).start(); } @Override - public List getContactByPerson(String id) { - List foundContacts = new ArrayList(); - for (ContactInfoModel contact : contactInfoModels) { - if (contact.getPersonId().equals(id)) { - foundContacts.add(contact); + public void getContactByPerson(PersonDetailsCallback callback, final String personId) { + final WeakReference weakReference = new WeakReference(callback); + new Thread(new Runnable() { + @Override + public void run() { + List foundContacts = new ArrayList(); + for (ContactInfoModel contact : contactInfoModels) { + if (contact.getPersonId().equals(personId)) { + foundContacts.add(contact); + } + } + PersonDetailsCallback local = weakReference.get(); + if (local != null) { + local.onFetchPersonContacts(contactInfoModels); + } } - } - return foundContacts; + }).start(); } @Override - public PersonModelAdvanced getPersonById(String id) { - for (PersonModelAdvanced personModelAdvanced : personModelAdvanceds) { - if (personModelAdvanced.getId().equals(id)) { - return personModelAdvanced; + public void getPersonById(PersonDetailsCallback callback, final String personId) { + final WeakReference weakReference = new WeakReference(callback); + new Thread(new Runnable() { + @Override + public void run() { + for (PersonModelAdvanced personModelAdvanced : personModelAdvanceds) { + if (personModelAdvanced.getId().equals(personId)) { + PersonDetailsCallback local = weakReference.get(); + if (local != null) { + local.onFetchPersonDetails(personModelAdvanced); + } + } + } + } - } - return null; + }).start(); } public Uri resourceToUri(int resID) { diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFromSystem.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFromSystem.java index 6a57da7..51e35d0 100644 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFromSystem.java +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/repository/ContactRepositoryFromSystem.java @@ -7,14 +7,16 @@ import android.provider.ContactsContract; import android.util.Log; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.a65apps.yuhnin.lesson1.callbacks.PersonDetailsCallback; +import com.a65apps.yuhnin.lesson1.callbacks.PersonListCallback; import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; import com.a65apps.yuhnin.lesson1.pojo.ContactType; import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -33,94 +35,117 @@ public static synchronized ContactRepositoryFromSystem getInstance(Context conte return instance; } - @Nullable @Override - public List getAllPersons() { - ArrayList personList = new ArrayList<>(); - ContentResolver contentResolver = context.getContentResolver(); - Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, - null, null, null, null); - try { - if (cursor != null) { - while (cursor.moveToNext()) { - try { - String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); - String displaName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); - String strPhotoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)); - Log.d(LOG_TAG, "Найден контакт: id=" + id + "; ФИО: " + displaName + " фото="+strPhotoUri); - personList.add(new PersonModelCompact(id, displaName, strPhotoUri==null ? null : Uri.parse(strPhotoUri))); - //Log.d(LOG_TAG, "Контакт добавлен"); - } catch (Exception e) { - Log.d(LOG_TAG, "Произошла ошибка получения контакта: " + e.getMessage()); + public void getAllPersons (PersonListCallback callback) { + final WeakReference weakReference = new WeakReference<>(callback); + new Thread(new Runnable() { + @Override + public void run() { + ArrayList personList = new ArrayList<>(); + ContentResolver contentResolver = context.getContentResolver(); + Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, + null, null, null, null); + try { + if (cursor != null) { + while (cursor.moveToNext()) { + try { + String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); + String displaName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); + String strPhotoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)); + Log.d(LOG_TAG, "Найден контакт: id=" + id + "; ФИО: " + displaName + " фото="+strPhotoUri); + personList.add(new PersonModelCompact(id, displaName, strPhotoUri==null ? null : Uri.parse(strPhotoUri))); + //Log.d(LOG_TAG, "Контакт добавлен"); + } catch (Exception e) { + Log.d(LOG_TAG, "Произошла ошибка получения контакта: " + e.getMessage()); + } + } + } + } finally { + if (cursor != null) { + cursor.close(); } } + PersonListCallback local = weakReference.get(); + if (local != null) { + local.getPersonList(personList); + } } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return personList; + }).start(); } - @NonNull @Override - public List getContactByPerson(String personId) { - List contactInfoModels = new ArrayList(); - try { - List phoneNumbers = getPhoneList(personId, context.getContentResolver()); - if (phoneNumbers != null) { - contactInfoModels.addAll(phoneNumbers); - } - } catch (Exception e) { - Log.e(LOG_TAG, "Произошла ошибка чтения списка телефонов контакта id=" + personId + - ". Текст ошибки: " + e.getMessage()); - } - - try { - List emails = getEmailList(personId, context.getContentResolver()); - if (emails != null) { - contactInfoModels.addAll(emails); + public void getContactByPerson(PersonDetailsCallback callback, final String personId) { + final WeakReference weakReference = new WeakReference(callback); + new Thread(new Runnable() { + @Override + public void run() { + List contactInfoModels = new ArrayList(); + try { + List phoneNumbers = getPhoneList(personId, context.getContentResolver()); + if (phoneNumbers != null) { + contactInfoModels.addAll(phoneNumbers); + } + } catch (Exception e) { + Log.e(LOG_TAG, "Произошла ошибка чтения списка телефонов контакта id=" + personId + + ". Текст ошибки: " + e.getMessage()); + } + try { + List emails = getEmailList(personId, context.getContentResolver()); + if (emails != null) { + contactInfoModels.addAll(emails); + } + } catch (Exception e) { + Log.e(LOG_TAG, "Произошла ошибка чтения списка адресов эл.почты контакта " + personId + + ". Текст ошибки: " + e.getMessage()); + } + PersonDetailsCallback local = weakReference.get(); + if (local != null) { + local.onFetchPersonContacts(contactInfoModels); + } } - } catch (Exception e) { - Log.e(LOG_TAG, "Произошла ошибка чтения списка адресов эл.почты контакта " + personId + - ". Текст ошибки: " + e.getMessage()); - } - - return contactInfoModels; + }).start(); } @Override - public PersonModelAdvanced getPersonById(String personId) { - PersonModelAdvanced personModelAdvanced = null; - ContentResolver contentResolver = context.getContentResolver(); - Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI,null, - ContactsContract.Contacts._ID + " = " + personId,null ,null); - try{ - if (cursor != null) { - cursor.moveToNext(); - String displaName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)); - String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); - String strPhotoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)); - String dateBirthDay = getDateBirthday(id, contentResolver); - String description = getCompanyName(id, contentResolver); - personModelAdvanced = new PersonModelAdvanced( - personId, - displaName, - description, - strPhotoUri == null ? null : Uri.parse(strPhotoUri), - dateBirthDay); - } - } catch (Exception e) { - Log.e(LOG_TAG, "Ошибка получения информации о контакте" + e.getMessage()); - } finally { - if (cursor != null) { - cursor.close(); + public void getPersonById(PersonDetailsCallback callback, final String personId) { + final WeakReference weakReference = new WeakReference(callback); + new Thread(new Runnable() { + @Override + public void run() { + PersonModelAdvanced personModelAdvanced = null; + ContentResolver contentResolver = context.getContentResolver(); + Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI,null, + ContactsContract.Contacts._ID + " = " + personId,null ,null); + try{ + if (cursor != null) { + cursor.moveToNext(); + String displaName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)); + String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); + String strPhotoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)); + String dateBirthDay = getDateBirthday(id, contentResolver); + String description = getCompanyName(id, contentResolver); + personModelAdvanced = new PersonModelAdvanced( + personId, + displaName, + description, + strPhotoUri == null ? null : Uri.parse(strPhotoUri), + dateBirthDay); + } + } catch (Exception e) { + Log.e(LOG_TAG, "Ошибка получения информации о контакте" + e.getMessage()); + } finally { + if (cursor != null) { + cursor.close(); + } + } + PersonDetailsCallback local = weakReference.get(); + if (local != null) { + local.onFetchPersonDetails(personModelAdvanced); + } } - } - return personModelAdvanced; + }).start(); } /** @@ -276,5 +301,4 @@ private List getEmailList(String personId, ContentResolver con } return emailList; } - } diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/services/DataFetchService.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/services/DataFetchService.java deleted file mode 100644 index a0919c9..0000000 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/services/DataFetchService.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.a65apps.yuhnin.lesson1.services; - -import android.app.Service; -import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; - -import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; -import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; -import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; -import com.a65apps.yuhnin.lesson1.repository.ContactRepositoryFromSystem; -import com.a65apps.yuhnin.lesson1.ui.listeners.ContactsResultListener; -import com.a65apps.yuhnin.lesson1.ui.listeners.PersonListResultListener; -import com.a65apps.yuhnin.lesson1.ui.listeners.PersonResultListener; - -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.Random; - -public class DataFetchService extends Service { - - // Экземпляр Binder для клиентов - private final IBinder mBinder = new LocalBinder(); - // Random number generator - private final Random mGenerator = new Random(); - - public DataFetchService() { - } - - - public class LocalBinder extends Binder { - public DataFetchService getService() { - return DataFetchService.this; - } - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - /** method for clients */ - public int getRandomNumber() { - return mGenerator.nextInt(100); - } - - public void fetchPersons(PersonListResultListener callback) { - final WeakReference ref = new WeakReference(callback); - new Thread(new Runnable() { - @Override - public void run() { - List personModelCompacts = ContactRepositoryFromSystem.getInstance(getApplicationContext()).getAllPersons(); - PersonListResultListener local = ref.get(); - if (local != null) { - local.onFetchPersonList(personModelCompacts); - } - } - }).start(); - } - - public void fetchPersonById(PersonResultListener callback, final String personId) { - final WeakReference ref = new WeakReference(callback); - new Thread(new Runnable() { - @Override - public void run() { - PersonModelAdvanced personModelAdvanced = ContactRepositoryFromSystem.getInstance(getApplicationContext()).getPersonById(personId); - PersonResultListener local = ref.get(); - if (local != null) { - local.onFetchPersonModel(personModelAdvanced); - } - } - }).start(); - } - - public void fetchContactInfo(ContactsResultListener callback, final String personId) { - final WeakReference ref = new WeakReference(callback); - new Thread(new Runnable() { - @Override - public void run() { - List contactInfoModels = ContactRepositoryFromSystem - .getInstance(getApplicationContext()).getContactByPerson(personId); - ContactsResultListener local = ref.get(); - if (local != null) { - local.onFetchContacts(contactInfoModels); - } - } - }).start(); - } - - -} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/activities/MainActivity.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/activities/MainActivity.java index fff2559..7ee16e5 100644 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/activities/MainActivity.java +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/activities/MainActivity.java @@ -23,7 +23,6 @@ import com.a65apps.yuhnin.lesson1.Constants; import com.a65apps.yuhnin.lesson1.R; -import com.a65apps.yuhnin.lesson1.services.DataFetchService; import com.a65apps.yuhnin.lesson1.ui.fragments.ContactDetailsFragment; import com.a65apps.yuhnin.lesson1.ui.fragments.ContactListFragment; import com.a65apps.yuhnin.lesson1.ui.fragments.RequestPermissonFragment; @@ -37,16 +36,12 @@ public class MainActivity extends AppCompatActivity final String TAG_FRAGMENT_DETAILS = "TAG_FRAGMENT_DETAILS"; final String TAG_FRAGMENT_PERM_REQ = "TAG_FRAGMENT_PERM_REQ"; final String TAG_FRAGMENT_LIST = "TAG_FRAGMENT_LIST"; - boolean mBound = false; FragmentManager fragmentManager = getSupportFragmentManager(); @Nullable Toolbar toolbar; - @Nullable - DataFetchService mService; - @Override protected void onStart() { Log.d(LOG_TAG, "onStart start"); @@ -63,7 +58,7 @@ protected void onDestroy() { @Override protected void onCreate(Bundle savedInstanceState) { - Log.d(LOG_TAG, "onCreate strat"); + Log.d(LOG_TAG, "onCreate start"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = findViewById(R.id.toolbar); @@ -73,7 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { int permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS); if (permissionStatus == PackageManager.PERMISSION_GRANTED) { if (fragmentManager.getFragments().isEmpty()) { - String id = getIntent().getStringExtra("KEY_PERSON_ID"); + String id = getIntent().getStringExtra(Constants.KEY_PERSON_ID); if (id != null && !id.isEmpty()) { сreateDetailsFragment(id); } else { @@ -116,7 +111,7 @@ private void createPersonListFragment() { */ private void сreateDetailsFragment(String personId) { Bundle bundle = new Bundle(); - bundle.putString("PERSON_ID", personId); + bundle.putString(Constants.KEY_PERSON_ID, personId); ContactDetailsFragment contactDetailsFragment = (ContactDetailsFragment) fragmentManager.findFragmentByTag(TAG_FRAGMENT_DETAILS); if (contactDetailsFragment == null) { // Фрагмент еще не создан @@ -216,7 +211,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis case Constants.CODE_PERMISSION_READ_CONTACTS: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(getApplicationContext(), getString(R.string.permission_granted), Toast.LENGTH_SHORT); - String id = getIntent().getStringExtra("KEY_PERSON_ID"); + String id = getIntent().getStringExtra(Constants.KEY_PERSON_ID); if (id != null && !id.isEmpty()) { сreateDetailsFragment(id); } else { diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/adapters/PersonListAdapter.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/adapters/PersonListAdapter.java index bccd240..2016a83 100644 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/adapters/PersonListAdapter.java +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/adapters/PersonListAdapter.java @@ -37,7 +37,6 @@ public Object getItem(int i) { @Override public long getItemId(int i) { - // FIXME: не уверен, что так делать правильно. Но мне больше негде взять численный идентификтор return this.personList.get(i).getId().hashCode(); } diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactDetailsFragment.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactDetailsFragment.java index 3c7b1f6..a34b355 100644 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactDetailsFragment.java +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactDetailsFragment.java @@ -2,17 +2,12 @@ import android.app.AlarmManager; import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - -import android.os.IBinder; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -24,25 +19,34 @@ import android.widget.ToggleButton; import com.a65apps.yuhnin.lesson1.BirthdayReminderReceiver; +import com.a65apps.yuhnin.lesson1.Constants; import com.a65apps.yuhnin.lesson1.R; import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; -import com.a65apps.yuhnin.lesson1.services.DataFetchService; +import com.a65apps.yuhnin.lesson1.presenters.ContactDetailsPresenter; +import com.a65apps.yuhnin.lesson1.repository.ContactRepositoryFromSystem; import com.a65apps.yuhnin.lesson1.ui.adapters.ContactListAdapter; -import com.a65apps.yuhnin.lesson1.ui.listeners.ContactsResultListener; import com.a65apps.yuhnin.lesson1.ui.listeners.EventActionBarListener; -import com.a65apps.yuhnin.lesson1.ui.listeners.PersonResultListener; +import com.a65apps.yuhnin.lesson1.views.ContactDetailsView; +import com.arellomobile.mvp.MvpAppCompatFragment; +import com.arellomobile.mvp.presenter.InjectPresenter; +import com.arellomobile.mvp.presenter.ProvidePresenter; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; -public class ContactDetailsFragment extends Fragment - implements ContactsResultListener, PersonResultListener, CompoundButton.OnCheckedChangeListener { - static final String ARG_PARAM_PERSON_ID = "PERSON_ID"; +public class ContactDetailsFragment extends MvpAppCompatFragment + implements ContactDetailsView, CompoundButton.OnCheckedChangeListener { final String LOG_TAG = "details_fragment"; - boolean serviceBound = false; + + ImageView ivAvatar; + TextView tvFullname; + ListView lvContacts; + TextView tvDescription; + TextView tvBirthday; + ToggleButton toggleBtnRemindBirthday; @NonNull PersonModelAdvanced person; @@ -55,22 +59,19 @@ public class ContactDetailsFragment extends Fragment private String personId = ""; - @Nullable - DataFetchService mService; - @Nullable List contactInfoList; @Nullable private EventActionBarListener eventActionBarListener; - ImageView ivAvatar; - TextView tvFullname; - ListView lvContacts; - TextView tvDescription; - TextView tvBirthday; - ToggleButton toggleBtnRemindBirthday; + @InjectPresenter + ContactDetailsPresenter contactDetailsPresenter; + @ProvidePresenter + ContactDetailsPresenter providerContactDetailsPresenter(){ + return contactDetailsPresenter = new ContactDetailsPresenter(ContactRepositoryFromSystem.getInstance(getActivity().getApplicationContext())); + } public ContactDetailsFragment() { // Required empty public constructor @@ -95,12 +96,8 @@ public void onDetach() { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { - this.personId = getArguments().getString(ARG_PARAM_PERSON_ID); + this.personId = getArguments().getString(Constants.KEY_PERSON_ID); } - // Биндинг сервиса - Intent intent = new Intent(getActivity(), DataFetchService.class); - getActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } @Override @@ -124,7 +121,8 @@ public void onResume() { if (eventActionBarListener != null) { eventActionBarListener.setVisibleToolBarBackButton(true); } - requestContactsByPerson(); + contactDetailsPresenter.requestContactsByPerson(personId); + contactDetailsPresenter.requestPersonDetails(personId); requireActivity().setTitle(getString(R.string.toolbar_header_person_details)); super.onResume(); } @@ -138,23 +136,6 @@ public void onDestroyView() { super.onDestroyView(); } - @Override - public void onDestroy() { - if (serviceBound) { - getActivity().unbindService(mConnection); - serviceBound = false; - } - super.onDestroy(); - } - - public void requestContactsByPerson() { - if (mService != null) { - mService.fetchPersonById(this, personId); - mService.fetchContactInfo(this, personId); - Log.d(LOG_TAG, "Запрашиваем детали контакта и его контактную информацию"); - } - } - private void updateFields() { if (person == null) { Log.e(LOG_TAG, "Невозможно отобразить данные контакта person=null"); @@ -183,39 +164,6 @@ private void updateFields() { } - @Override - public void onFetchContacts(final List contactInfoList) { - this.contactInfoList = contactInfoList; - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - if (lvContacts != null && contactInfoList != null) { - ContactListAdapter contactListAdapter = new ContactListAdapter(getContext(), contactInfoList); - lvContacts.setAdapter(contactListAdapter); - } else { - Log.e(LOG_TAG, "onFetchContacts - Сервис вернул contactInfoList=null"); - } - } - }); - } - - @Override - public void onFetchPersonModel(final PersonModelAdvanced personModelAdvanced) { - this.person = personModelAdvanced; - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - if (personModelAdvanced != null) { - Log.d(LOG_TAG, "Создаем список контактных данных контакта " + person.getFullName()); - updateFields(); - } else { - Log.e(LOG_TAG, "onFetchPersonModel - Сервис вернул personModels=null"); - } - } - }); - - } - @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { @@ -223,7 +171,6 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { } else { Log.d(LOG_TAG, "Toggle uncheked"); } - setBirthdayReminderEnabled(isChecked); } @@ -276,19 +223,25 @@ private boolean checkBirthdayReminder() { PendingIntent.FLAG_NO_CREATE) != null); } - private ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - DataFetchService.LocalBinder binder = (DataFetchService.LocalBinder) service; - mService = binder.getService(); - Log.d(LOG_TAG, "Сработал ServiceConnection - onServiceConnected"); - requestContactsByPerson(); + @Override + public void getContactDetails(PersonModelAdvanced personModel) { + this.person = personModel; + if (person != null) { + Log.d(LOG_TAG, "Создаем список контактных данных контакта " + person.getFullName()); + updateFields(); + } else { + Log.e(LOG_TAG, "onFetchPersonModel - репозиторий вернул personModels=null"); } + } - @Override - public void onServiceDisconnected(ComponentName arg0) { - Log.d(LOG_TAG, "Сработал ServiceConnection - onServiceDisconnected"); + @Override + public void getContactsInfo(List listOfContacts) { + this.contactInfoList = listOfContacts; + if (lvContacts != null && contactInfoList != null) { + ContactListAdapter contactListAdapter = new ContactListAdapter(getContext(), contactInfoList); + lvContacts.setAdapter(contactListAdapter); + } else { + Log.e(LOG_TAG, "onFetchContacts - репозиторий вернул contactInfoList=null"); } - }; + } } diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactListFragment.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactListFragment.java index e3838f2..24751c7 100644 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactListFragment.java +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/fragments/ContactListFragment.java @@ -1,15 +1,10 @@ package com.a65apps.yuhnin.lesson1.ui.fragments; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Bundle; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import android.os.IBinder; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -19,40 +14,44 @@ import com.a65apps.yuhnin.lesson1.R; import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; -import com.a65apps.yuhnin.lesson1.services.DataFetchService; +import com.a65apps.yuhnin.lesson1.presenters.ContactListPresenter; +import com.a65apps.yuhnin.lesson1.repository.ContactRepositoryFromSystem; import com.a65apps.yuhnin.lesson1.ui.adapters.PersonListAdapter; import com.a65apps.yuhnin.lesson1.ui.listeners.EventActionBarListener; import com.a65apps.yuhnin.lesson1.ui.listeners.OnPersonClickedListener; -import com.a65apps.yuhnin.lesson1.ui.listeners.PersonListResultListener; +import com.a65apps.yuhnin.lesson1.views.ContactListView; +import com.arellomobile.mvp.MvpAppCompatFragment; +import com.arellomobile.mvp.presenter.InjectPresenter; +import com.arellomobile.mvp.presenter.ProvidePresenter; import java.util.List; /** * Фрагмент списка контактов */ -// parent activity will implement this method to respond to click events - - - -public class ContactListFragment extends Fragment implements PersonListResultListener { +public class ContactListFragment extends MvpAppCompatFragment implements ContactListView { final String LOG_TAG = "contact_list_fragment"; - boolean serviceBound = false; ListView listviewPersons; @Nullable PersonListAdapter personListAdapter; - @Nullable - DataFetchService mService; - @Nullable private OnPersonClickedListener onPersonClickedListener; @Nullable private EventActionBarListener eventActionBarListener; + @InjectPresenter + ContactListPresenter contactListPresenter; + + @ProvidePresenter + ContactListPresenter providerContactListPresenter(){ + return contactListPresenter = new ContactListPresenter(ContactRepositoryFromSystem.getInstance(getActivity().getApplicationContext())); + } + @Override public void onAttach(@Nullable Context context) { Log.d(LOG_TAG, "onAttach"); @@ -79,34 +78,10 @@ public ContactListFragment() { } - public void requestPersonList() { - if (mService != null) { - mService.fetchPersons(this); - Log.d(LOG_TAG, "Запрашиваем getPersonList"); - } - } - - private void createPersonsListView(final List personList) { - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - if (personList != null && listviewPersons != null) { - Log.d(LOG_TAG, "Создаем список контактов " + personList.size()); - personListAdapter = new PersonListAdapter(getActivity(), personList); - listviewPersons.setAdapter(personListAdapter); - } - } - }); - - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(LOG_TAG, "onCreate"); - // Биндинг сервиса - Intent intent = new Intent(getActivity(), DataFetchService.class); - getActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override @@ -125,6 +100,7 @@ public void onItemClick(AdapterView parent, View view, } }); requireActivity().setTitle(getString(R.string.toolbar_header_person_list)); + contactListPresenter.requestContactList(); return view; } @@ -147,35 +123,12 @@ public void onDestroyView() { } @Override - public void onDestroy() { - if (serviceBound) { - getActivity().unbindService(mConnection); - serviceBound = false; + public void getContactList(final List personList) { + if (personList != null && listviewPersons != null) { + Log.d(LOG_TAG, "Создаем список контактов " + personList.size()); + personListAdapter = new PersonListAdapter(getActivity(), personList); + listviewPersons.setAdapter(personListAdapter); } - super.onDestroy(); - } - - @Override - public void onFetchPersonList(List personList) { - Log.d(LOG_TAG, "ПОЛУЧЕНЫ ДАННЫЕ СПИСКА КОНТАКТОВ"); - createPersonsListView(personList); } - - private ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - DataFetchService.LocalBinder binder = (DataFetchService.LocalBinder) service; - mService = binder.getService(); - serviceBound = true; - Log.d(LOG_TAG, "Сработал ServiceConnection - onServiceConnected"); - requestPersonList(); - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - Log.d(LOG_TAG, "Сработал ServiceConnection - onServiceDisconnected"); - } - }; } diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/ContactsResultListener.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/ContactsResultListener.java deleted file mode 100644 index 625f683..0000000 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/ContactsResultListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.a65apps.yuhnin.lesson1.ui.listeners; - -import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; -import java.util.List; - -public interface ContactsResultListener { - void onFetchContacts(List contactInfoModels); -} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/PersonListResultListener.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/PersonListResultListener.java deleted file mode 100644 index 8e9394a..0000000 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/PersonListResultListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.a65apps.yuhnin.lesson1.ui.listeners; - -import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; - -import java.util.List; - -public interface PersonListResultListener { - void onFetchPersonList(List personModelCompacts); -} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/PersonResultListener.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/PersonResultListener.java deleted file mode 100644 index 60fa909..0000000 --- a/app/src/main/java/com/a65apps/yuhnin/lesson1/ui/listeners/PersonResultListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.a65apps.yuhnin.lesson1.ui.listeners; - -import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; - -public interface PersonResultListener { - void onFetchPersonModel(PersonModelAdvanced personModelAdvanced); -} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/views/ContactDetailsView.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/views/ContactDetailsView.java new file mode 100644 index 0000000..5b5a429 --- /dev/null +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/views/ContactDetailsView.java @@ -0,0 +1,17 @@ +package com.a65apps.yuhnin.lesson1.views; + +import com.a65apps.yuhnin.lesson1.pojo.ContactInfoModel; +import com.a65apps.yuhnin.lesson1.pojo.PersonModelAdvanced; +import com.arellomobile.mvp.MvpView; +import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy; +import com.arellomobile.mvp.viewstate.strategy.StateStrategyType; + +import java.util.List; + +public interface ContactDetailsView extends MvpView { + @StateStrategyType(AddToEndSingleStrategy.class) + void getContactDetails(PersonModelAdvanced personModel); + + @StateStrategyType(AddToEndSingleStrategy.class) + void getContactsInfo(List listOfContacts); +} diff --git a/app/src/main/java/com/a65apps/yuhnin/lesson1/views/ContactListView.java b/app/src/main/java/com/a65apps/yuhnin/lesson1/views/ContactListView.java new file mode 100644 index 0000000..77cfb2d --- /dev/null +++ b/app/src/main/java/com/a65apps/yuhnin/lesson1/views/ContactListView.java @@ -0,0 +1,13 @@ +package com.a65apps.yuhnin.lesson1.views; + +import com.a65apps.yuhnin.lesson1.pojo.PersonModelCompact; +import com.arellomobile.mvp.MvpView; +import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy; +import com.arellomobile.mvp.viewstate.strategy.StateStrategyType; + +import java.util.List; + +public interface ContactListView extends MvpView { + @StateStrategyType(AddToEndSingleStrategy.class) + void getContactList(List personList); +}