diff --git a/app/build.gradle b/app/build.gradle index 4f454dd..6bb70c6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.uam.wmi.findmytutor" minSdkVersion 22 targetSdkVersion 27 - versionCode 17 - versionName "0.9.2-alpha" + versionCode 30 + versionName "0.9.6-beta" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } diff --git a/app/release/release/app.aab b/app/release/release/app.aab index aa09d96..0b58fd8 100644 Binary files a/app/release/release/app.aab and b/app/release/release/app.aab differ diff --git a/app/release/release/fmtBeta0.9.5v18.aab b/app/release/release/fmtBeta0.9.5v18.aab new file mode 100644 index 0000000..fb0edf9 Binary files /dev/null and b/app/release/release/fmtBeta0.9.5v18.aab differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5ccbfb9..d209b5a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,10 +27,10 @@ tools:ignore="AllowBackup,GoogleAppIndexingWarning"> + android:theme="@style/AppTheme.NoActionBar"> @@ -38,33 +38,43 @@ + android:screenOrientation="portrait" /> + android:screenOrientation="portrait" /> + android:configChanges="keyboardHidden|orientation|screenSize" + android:screenOrientation="portrait" /> - + android:theme="@style/AppTheme" /> + + \ No newline at end of file diff --git a/app/src/main/assets/wmi1floor.geojson b/app/src/main/assets/wmi1floor.geojson new file mode 100644 index 0000000..f099ad5 --- /dev/null +++ b/app/src/main/assets/wmi1floor.geojson @@ -0,0 +1,727 @@ +{ + "features": [ + { + "type": "Feature", + "properties": { + "name": "Pokoje profesorskie" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926707, + 52.46657 + ], + [ + 16.92652, + 52.466244 + ], + [ + 16.926459, + 52.466255 + ], + [ + 16.926645, + 52.466582 + ], + [ + 16.926707, + 52.46657 + ] + ] + ], + "type": "Polygon" + }, + "id": "07f45cd94d45bb5ad0b6b285b0f6fbbb" + }, + { + "type": "Feature", + "properties": { + "name": "Skrzydło B" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926961, + 52.467026 + ], + [ + 16.92677, + 52.466682 + ], + [ + 16.92652, + 52.466244 + ], + [ + 16.926459, + 52.466255 + ], + [ + 16.926428, + 52.466203 + ], + [ + 16.926175, + 52.46625 + ], + [ + 16.92621, + 52.466306 + ], + [ + 16.926323, + 52.466282 + ], + [ + 16.926515, + 52.466632 + ], + [ + 16.926408, + 52.466654 + ], + [ + 16.92644, + 52.466712 + ], + [ + 16.926543, + 52.46669 + ], + [ + 16.926754, + 52.467067 + ], + [ + 16.926961, + 52.467026 + ] + ] + ], + "type": "Polygon" + }, + "id": "143facf35f322434cfc5776f70f1db36" + }, + { + "type": "Feature", + "properties": { + "name": "Winda" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.92669, + 52.467141 + ], + [ + 16.92666, + 52.467089 + ], + [ + 16.926755, + 52.46707 + ], + [ + 16.926786, + 52.467121 + ], + [ + 16.92669, + 52.467141 + ] + ] + ], + "type": "Polygon" + }, + "id": "1a35bfafd619b80ffb8b36f03549e9e3" + }, + { + "type": "Feature", + "properties": { + "name": "D2" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926876, + 52.466659 + ], + [ + 16.926984, + 52.466635 + ], + [ + 16.92694, + 52.466557 + ], + [ + 16.926831, + 52.46658 + ], + [ + 16.926876, + 52.466659 + ] + ] + ], + "type": "Polygon" + }, + "id": "3e0a32a0583254e9e7c47fea1f402472" + }, + { + "type": "Feature", + "properties": { + "name": "Sale A" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.92714, + 52.466489 + ], + [ + 16.926925, + 52.466127 + ], + [ + 16.927027, + 52.466106 + ], + [ + 16.927232, + 52.466472 + ], + [ + 16.92714, + 52.466489 + ] + ] + ], + "type": "Polygon" + }, + "id": "3e56cb54baf90049a3ce2f16577c2cd8" + }, + { + "type": "Feature", + "properties": { + "name": "D1" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926984, + 52.466635 + ], + [ + 16.927084, + 52.466614 + ], + [ + 16.92704, + 52.466536 + ], + [ + 16.92694, + 52.466557 + ], + [ + 16.926984, + 52.466635 + ] + ] + ], + "type": "Polygon" + }, + "id": "56c2a7ab4e4d2ac14a46448d523470dc" + }, + { + "type": "Feature", + "properties": { + "name": "Hol" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.927104, + 52.467081 + ], + [ + 16.927039, + 52.467095 + ], + [ + 16.9268, + 52.467145 + ], + [ + 16.926754, + 52.467067 + ], + [ + 16.926961, + 52.467026 + ], + [ + 16.927277, + 52.46696 + ], + [ + 16.927272, + 52.466951 + ], + [ + 16.927451, + 52.466918 + ], + [ + 16.927494, + 52.467002 + ], + [ + 16.927278, + 52.467047 + ], + [ + 16.927144, + 52.467073 + ], + [ + 16.927104, + 52.467081 + ] + ] + ], + "type": "Polygon" + }, + "id": "6f1db27a4df5a46f7a6913556b01effe" + }, + { + "type": "Feature", + "properties": { + "name": "Pokoje profesorskie" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.92677, + 52.466683 + ], + [ + 16.926961, + 52.467026 + ], + [ + 16.926887, + 52.467041 + ], + [ + 16.926695, + 52.466699 + ], + [ + 16.92677, + 52.466683 + ] + ] + ], + "type": "Polygon" + }, + "id": "94f310dcaa6cee75d9e7800bf8d94155" + }, + { + "type": "Feature", + "properties": { + "name": "Biblioteka" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926801, + 52.467144 + ], + [ + 16.926971, + 52.467424 + ], + [ + 16.926956, + 52.46743 + ], + [ + 16.926944, + 52.467436 + ], + [ + 16.92693, + 52.467441 + ], + [ + 16.926903, + 52.467452 + ], + [ + 16.926861, + 52.467464 + ], + [ + 16.926778, + 52.467476 + ], + [ + 16.926731, + 52.467485 + ], + [ + 16.926567, + 52.467194 + ], + [ + 16.926801, + 52.467144 + ] + ] + ], + "type": "Polygon" + }, + "id": "aadab5775bdeb4eaf82c940255ddadd7" + }, + { + "type": "Feature", + "properties": { + "name": "Sale A" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.927082, + 52.466492 + ], + [ + 16.926999, + 52.466509 + ], + [ + 16.926771, + 52.466109 + ], + [ + 16.926857, + 52.466088 + ], + [ + 16.927082, + 52.466492 + ] + ] + ], + "type": "Polygon" + }, + "id": "b39857ed605e5794fa6c9cdee5d7d6cf" + }, + { + "type": "Feature", + "properties": { + "name": "Pokoje profesorskie" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926754, + 52.467066 + ], + [ + 16.926814, + 52.467054 + ], + [ + 16.926605, + 52.466676 + ], + [ + 16.926542, + 52.46669 + ], + [ + 16.926564, + 52.466728 + ], + [ + 16.926754, + 52.467066 + ] + ] + ], + "type": "Polygon" + }, + "id": "b754d867fde3826e72fe8c243399ff26" + }, + { + "type": "Feature", + "properties": { + "name": "Sale A" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.927272, + 52.46695 + ], + [ + 16.927083, + 52.466613 + ], + [ + 16.927167, + 52.466595 + ], + [ + 16.92735, + 52.466936 + ], + [ + 16.927272, + 52.46695 + ] + ] + ], + "type": "Polygon" + }, + "id": "c3726b2a2b3a589995fd3f17eecd1f53" + }, + { + "type": "Feature", + "properties": { + "name": "D3" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.92677, + 52.466682 + ], + [ + 16.926876, + 52.466659 + ], + [ + 16.926832, + 52.46658 + ], + [ + 16.926725, + 52.466604 + ], + [ + 16.92677, + 52.466682 + ] + ] + ], + "type": "Polygon" + }, + "id": "c73d9bdbccc15d9907e24b8bb731118a" + }, + { + "type": "Feature", + "properties": { + "name": "Pokoje profesorskie" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926324, + 52.466282 + ], + [ + 16.926503, + 52.46661 + ], + [ + 16.926579, + 52.466595 + ], + [ + 16.926398, + 52.466268 + ], + [ + 16.926324, + 52.466282 + ] + ] + ], + "type": "Polygon" + }, + "id": "d54475cedfa0a866e13933c56bf35d76" + }, + { + "type": "Feature", + "properties": { + "name": "Sale A" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.927607, + 52.466891 + ], + [ + 16.92734, + 52.466562 + ], + [ + 16.927242, + 52.466585 + ], + [ + 16.927451, + 52.466918 + ], + [ + 16.927607, + 52.466891 + ] + ] + ], + "type": "Polygon" + }, + "id": "d97989e446002de50bc8844d2d9cdf5c" + }, + { + "type": "Feature", + "properties": { + "name": "Winda" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.92669, + 52.467141 + ], + [ + 16.92666, + 52.467089 + ], + [ + 16.926755, + 52.46707 + ], + [ + 16.926786, + 52.467121 + ], + [ + 16.92669, + 52.467141 + ] + ] + ], + "type": "Polygon" + }, + "id": "dfdba4ee1ee0c55193b8dd04f8b00f6c" + }, + { + "type": "Feature", + "properties": { + "name": "Skrzydło A" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.927606, + 52.466891 + ], + [ + 16.927339, + 52.466562 + ], + [ + 16.927242, + 52.466585 + ], + [ + 16.92718, + 52.466482 + ], + [ + 16.927232, + 52.466473 + ], + [ + 16.927025, + 52.466105 + ], + [ + 16.926924, + 52.466126 + ], + [ + 16.926896, + 52.466078 + ], + [ + 16.92677, + 52.466108 + ], + [ + 16.926998, + 52.466509 + ], + [ + 16.927041, + 52.466537 + ], + [ + 16.927075, + 52.466595 + ], + [ + 16.927082, + 52.466614 + ], + [ + 16.927277, + 52.46696 + ], + [ + 16.927358, + 52.466942 + ], + [ + 16.927354, + 52.466935 + ], + [ + 16.92745, + 52.466918 + ], + [ + 16.927606, + 52.466891 + ] + ] + ], + "type": "Polygon" + }, + "id": "faaa569e3642a741f70a77071f19f131" + } + ], + "type": "FeatureCollection" +} \ No newline at end of file diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java index 3a5956e..5792051 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java @@ -53,6 +53,7 @@ import io.reactivex.schedulers.Schedulers; import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; import static com.uam.wmi.findmytutor.utils.Const.defaultMapZoom; +import static com.uam.wmi.findmytutor.utils.Const.onlineBackgroundLocationInterval; import static com.uam.wmi.findmytutor.utils.Const.searchMapZoom; import static com.uam.wmi.findmytutor.utils.PrefUtils.storeBackgroundLocationStatus; @@ -99,11 +100,11 @@ public abstract class BaseActivity String itemName = (String) item.getTitle(); Intent launchIntent; if (itemName.equals(getResources().getString(R.string.navigation_item_whitelist))) { - /* launchIntent = new Intent(getApplicationContext(), WhitelistActivity.class); - startActivity(launchIntent);*/ + launchIntent = new Intent(getApplicationContext(), WhiteList.class); + startActivity(launchIntent); } else if (itemName.equals(getResources().getString(R.string.navigation_item_blacklist))) { - /* launchIntent = new Intent(getApplicationContext(), BlacklistActivity.class); - startActivity(launchIntent);*/ + launchIntent = new Intent(getApplicationContext(),BlackList.class); + startActivity(launchIntent); } else if (itemName.equals(getResources().getString(R.string.navigation_item_profile))) { @@ -200,7 +201,6 @@ public abstract class BaseActivity public void stopBackgroundLocalizationTask() { Intent stopIntent = new Intent(getApplicationContext(), BackgroundLocalizationService.class); stopIntent.putExtra("request_stop", true); - Log.e("Localization", "JEstem w stop BG"); stopService(stopIntent); @@ -210,6 +210,7 @@ public abstract class BaseActivity checkPermissions(); Intent startIntent = new Intent(getApplicationContext(), BackgroundLocalizationService.class); + startIntent.putExtra("notify_interval", onlineBackgroundLocationInterval); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { startForegroundService(startIntent); @@ -219,17 +220,12 @@ public abstract class BaseActivity } public void handleBackgroundTaskLifeCycle() { - Log.e("Localization", String.valueOf(PrefUtils.isEnableSharingLocalization(getApplicationContext()))); Boolean shouldServiceRun = PrefUtils.isEnableSharingLocalization(getApplicationContext()) && isTutor; - Log.e("Localization", String.valueOf(shouldServiceRun)); if (shouldServiceRun) { startBackgroundLocalizationTask(); - Log.e("Localization", "JEstem i odpalam"); - } else { stopBackgroundLocalizationTask(); - Log.e("Localization", "JEstem i nie odpalam"); } } @@ -285,7 +281,6 @@ public abstract class BaseActivity getMenuInflater().inflate(R.menu.menu_main, menu); infoMenuItem = menu.findItem(R.id.action_info); - MenuItem myActionMenuItem = menu.findItem(R.id.action_search); searchView = (SearchView) myActionMenuItem.getActionView(); @@ -295,6 +290,10 @@ public abstract class BaseActivity adjustMapToSearch(defaultMapZoom); } + if (!hasFocus && activeFragment.equals(ActiveFragment.USER_LIST)) { + ((UsersListFragment) userListFragment).restoreUsersList(); + } + if(hasFocus && activeFragment.equals(ActiveFragment.NONE)){ adjustMapToSearch(searchMapZoom); } @@ -303,6 +302,7 @@ public abstract class BaseActivity RxSearchObservable.fromView(searchView) .skip(0) .map(String::toLowerCase) + .filter(t -> !t.isEmpty()) .debounce(250, TimeUnit.MILLISECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/BlackList.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/BlackList.java new file mode 100644 index 0000000..766bf8b --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/BlackList.java @@ -0,0 +1,348 @@ +package com.uam.wmi.findmytutor.activity; + +import android.annotation.SuppressLint; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.design.widget.CoordinatorLayout; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.Switch; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ToggleButton; + +import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.uam.wmi.findmytutor.R; +import com.uam.wmi.findmytutor.adapters.BlackListAdapter; +import com.uam.wmi.findmytutor.model.IsUsingListBool; +import com.uam.wmi.findmytutor.model.PredefinedCoordViewModel; +import com.uam.wmi.findmytutor.model.StudentIdModel; +import com.uam.wmi.findmytutor.model.User; +import com.uam.wmi.findmytutor.model.UserResponseModel; +import com.uam.wmi.findmytutor.network.ApiClient; +import com.uam.wmi.findmytutor.service.PredefinedStatusesService; +import com.uam.wmi.findmytutor.service.UserService; +import com.uam.wmi.findmytutor.utils.MyDividerItemDecoration; +import com.uam.wmi.findmytutor.utils.PrefUtils; +import com.uam.wmi.findmytutor.utils.RecyclerTouchListener; +import com.uam.wmi.findmytutor.utils.RestApiHelper; +import com.uam.wmi.findmytutor.utils.SharingLevel; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.observers.DisposableSingleObserver; +import io.reactivex.schedulers.Schedulers; +import okhttp3.ResponseBody; + +public class BlackList extends AppCompatActivity { + + private CompositeDisposable disposable = new CompositeDisposable(); + private UserService userService; + private boolean didFetched = false; + private String tutorId; + + @BindView(R.id.recycler_view_blacklist) + RecyclerView recyclerView; + @BindView(R.id.black_list_empty_text_view) + TextView noNotesView; + @BindView(R.id.switch_blacklist_toggle) + Switch aSwitch; + @BindView(R.id.add_to_black_list_fab) + FloatingActionButton addToBlackListFab; + + private BlackListAdapter mAdapter; + private List blacklistedUsers = new ArrayList<>(); + private HashSet blacklistedUsersIDs = new HashSet<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + setContentView(R.layout.activity_black_list); + ButterKnife.bind(this); + tutorId = PrefUtils.getUserId(getApplicationContext()); + userService = ApiClient.getClient(getApplicationContext()) + .create(UserService.class); + + if (PrefUtils.isBlackListing(this)){ + aSwitch.setText(getString(R.string.action_black_list) +" ON"); + aSwitch.setChecked(true); + handleChangeRequest(true); + }else{ + aSwitch.setText(getString(R.string.action_black_list) +" OFF"); + aSwitch.setChecked(false); + handleChangeRequest(false); + } + + Toolbar toolbar = findViewById(R.id.toolbar); + toolbar.setTitle(getString(R.string.activity_title_blacklist)); + setSupportActionBar(toolbar); + + mAdapter = new BlackListAdapter(this, blacklistedUsers); + RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); + recyclerView.setLayoutManager(mLayoutManager); + recyclerView.setItemAnimator(new DefaultItemAnimator()); + recyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16)); + recyclerView.setAdapter(mAdapter); + + /** + * On long press on RecyclerView item, open alert dialog + * with options to choose + * Edit and Delete + * */ + recyclerView.addOnItemTouchListener(new RecyclerTouchListener(this, + recyclerView, new RecyclerTouchListener.ClickListener() { + @Override + public void onClick(View view, final int position) { + } + + @Override + public void onLongClick(View view, int position) { + } + })); + + addToBlackListFab.setOnClickListener(this::showFabDialog); + + fetchBlackListedUsersIDs(PrefUtils.getUserId(getApplicationContext())); + handleSwitch(); + } + + private void fetchBlackListedUsersIDs(String userId) { + disposable.add( + userService.getTutorBlacklistedByID(userId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(new DisposableSingleObserver>() { + @Override + public void onSuccess(List users) { + blacklistedUsersIDs.addAll(users); + didFetched = true; + fetchBlackListedUsers(); + toggleEmptyNotes(); + } + + @Override + public void onError(Throwable e) { + showError(e); + } + }) + ); + } + + private void fetchBlackListedUsers() { + for (String GUID : blacklistedUsersIDs){ + disposable.add( + userService.getUserById(GUID) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(new DisposableSingleObserver() { + @Override + public void onSuccess(User user) { + blacklistedUsers.add(user); + toggleEmptyNotes(); + if (blacklistedUsers.size() == blacklistedUsersIDs.size()) { + mAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onError(Throwable e) { + showError(e); + } + }) + ); + } + } + + + private void showFabDialog(View v){ + LayoutInflater layoutInflaterAndroid = LayoutInflater.from(getApplicationContext()); + @SuppressLint("InflateParams") View view = layoutInflaterAndroid.inflate(R.layout.black_list_fab_modal, null); + AlertDialog.Builder alertDialogBuilderUserInput = new android.support.v7.app.AlertDialog.Builder(this); + + alertDialogBuilderUserInput.setView(view).setPositiveButton(getApplicationContext().getString(R.string.modal_location_send), null); + + alertDialogBuilderUserInput + .setPositiveButton(R.string.add, null) + .setNegativeButton(R.string.cancel, null); + + final AlertDialog alertDialog = alertDialogBuilderUserInput.create(); + alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + EditText modalUserInput = view.findViewById(R.id.black_list_modal_input); + + alertDialog.setOnShowListener(dialogInterface -> { + Button sendButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + + Button dismissButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE); + + dismissButton.setOnClickListener(view1 -> alertDialog.dismiss()); + + sendButton.setOnClickListener(view1 -> { + String body = modalUserInput.getText().toString(); + + if (TextUtils.isEmpty(body)) { + Toast.makeText(getApplicationContext(), R.string.can_not_be_empty, Toast.LENGTH_SHORT).show(); + modalUserInput.requestFocus(); + } else { + sendUserToBlacklist(body); + alertDialog.dismiss(); + } + }); + }); + + alertDialog.show(); + } + + private void sendUserToBlacklist(String body) { + StudentIdModel studentIdModel = new StudentIdModel(body); + disposable.add( + userService.addStudentToBlacklist(PrefUtils.getUserId(getApplicationContext()), studentIdModel) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::handleAddUser,this::showError) + ); + } + + + private void handleAddUser(User user) { + blacklistedUsersIDs.clear(); + blacklistedUsers.clear(); + blacklistedUsersIDs.addAll(user.getBlacklist()); + fetchBlackListedUsers(); + } + + private void showError(Throwable e) { + String message; + + if (e instanceof HttpException) { + ResponseBody responseBody = ((HttpException) e).response().errorBody(); + message = RestApiHelper.getErrorMessage(responseBody); + if (((HttpException) e).response().code() == 404) { + message = getString(R.string.no_such_a_user); + } + } else { + message = "Network Error !"; + } + + Toast.makeText(this, message, Snackbar.LENGTH_LONG).show(); + } + + private void toggleEmptyNotes() { + + if (didFetched && blacklistedUsers.size() == 0) { + noNotesView.setText(R.string.list_is_empty); + noNotesView.setVisibility(View.VISIBLE); + }else if (blacklistedUsers.size() > 0) { + noNotesView.setVisibility(View.GONE); + } else { + noNotesView.setText(getString(R.string.loading)); + noNotesView.setVisibility(View.VISIBLE); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + disposable.dispose(); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_black_list, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + if (item.getItemId()==R.id.action_blacklist_info_popup){ + int layoutID = R.layout.info_popup_blacklist; + + View popupView = getLayoutInflater().inflate(layoutID,null); + + PopupWindow popupWindow = new PopupWindow(popupView, + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + // If the PopupWindow should be focusable + popupWindow.setFocusable(true); + // If you need the PopupWindow to dismiss when when touched outside + popupWindow.setBackgroundDrawable(new ColorDrawable()); + // Get the View's(the one that was clicked in the Fragment) location + View anchorView= getWindow().getDecorView().findViewById(android.R.id.content); + popupWindow.showAtLocation(anchorView,Gravity.TOP|Gravity.END, 0, 0); + } + + return super.onOptionsItemSelected(item); + } + + private void handleSwitch(){ + aSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (isChecked){ + aSwitch.setText(getString(R.string.action_black_list) + " "+ getString(R.string.on)); + handleChangeRequest(true); + PrefUtils.useBlacklist(this,true); + }else { + aSwitch.setText(getString(R.string.action_black_list) + " "+getString(R.string.off)); + handleChangeRequest(false); + PrefUtils.useBlacklist(this,false); + } + }); + } + + private void handleChangeRequest(boolean value){ + IsUsingListBool isUsingListBool = new IsUsingListBool(); + isUsingListBool.setIsUsing(value); + disposable.add( + userService.setTutorBlacklist(tutorId, isUsingListBool) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(()->{ + },this::showError) + ); + } +} diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java index b70d370..d02a6f5 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java @@ -334,7 +334,7 @@ public class MapActivity extends BaseActivity final AlertDialog alertDialog = alertDialogBuilderUserInput.create(); - EditText modalUserInput = view.findViewById(R.id.feedback_input); + EditText modalUserInput = view.findViewById(R.id.manual_input); alertDialog.setOnShowListener(dialogInterface -> { Button sendButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java index 30b25e3..ddeb3f1 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java @@ -213,6 +213,8 @@ public class SharingFragment extends PreferenceFragment implements SharedPrefere /** Sharing level list **/ locationMode.setOnPreferenceChangeListener((preference, newValue) -> { + ((MapActivity) getActivity()).stopBackgroundLocalizationTask(); + ((MapActivity) getActivity()).startBackgroundLocalizationTask(); PrefUtils.storeLocationMode(getApplicationContext(), locationLevelMapping.get(Integer.parseInt((String) newValue))); if (PrefUtils.getLocationLevel(getApplicationContext()).equals(SharingLevel.MANUAL.toString())) { diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/SpecialList.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/SpecialList.java new file mode 100644 index 0000000..ec12a92 --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/SpecialList.java @@ -0,0 +1,19 @@ +package com.uam.wmi.findmytutor.activity; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.uam.wmi.findmytutor.R; + +public abstract class SpecialList extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setContentView(getContentViewId()); + + } + + abstract int getContentViewId(); + + +} diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/TutorTab.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/TutorTab.java index 288f269..4db2335 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/TutorTab.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/TutorTab.java @@ -8,18 +8,13 @@ import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; -import android.widget.Adapter; import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; -import com.annimon.stream.Stream; import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; import com.uam.wmi.findmytutor.R; import com.uam.wmi.findmytutor.adapters.DutyHoursAdapter; -import com.uam.wmi.findmytutor.model.DutyHour; import com.uam.wmi.findmytutor.model.DutyHourViewModel; import com.uam.wmi.findmytutor.model.TutorTabViewModel; import com.uam.wmi.findmytutor.model.User; @@ -36,11 +31,14 @@ import java.util.List; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.observers.DisposableCompletableObserver; import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.schedulers.Schedulers; import okhttp3.ResponseBody; import retrofit2.Response; +import static java.lang.String.valueOf; + public class TutorTab extends AppCompatActivity { private TutorTabApi tutorTabService; private UserService userService; @@ -53,6 +51,7 @@ public class TutorTab extends AppCompatActivity { private TextView department; private Button addDutyButton; private Button saveButon; + private Button scrapButton; private Boolean ifTutorTabExists = true; private List dutyHourList; private RecyclerView dutyHoursRecycller; @@ -72,69 +71,24 @@ public class TutorTab extends AppCompatActivity { userEmail = findViewById(R.id.userEmail); department = findViewById(R.id.userDepartment); saveButon = findViewById(R.id.saveButon); + scrapButton = findViewById(R.id.scrapTutorTab); addDutyButton = findViewById(R.id.addDuty); - dutyHoursRecycller = (RecyclerView) findViewById(R.id.dutyHourView); + dutyHoursRecycller = findViewById(R.id.dutyHourView); dutyHoursLayoutManager = new LinearLayoutManager(this); dutyHoursRecycller.setLayoutManager(dutyHoursLayoutManager); - tutorTabService = ApiClient.getClient(getApplicationContext()) .create(TutorTabApi.class); userService = ApiClient.getClient(getApplicationContext()) .create(UserService.class); - disposable.add( - tutorTabService.apiUsersTutorTabByTutorIdGet(PrefUtils.getUserId(getApplicationContext())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableSingleObserver() { - @Override - public void onSuccess(TutorTabViewModel tutorTabViewModel) { - - dutyHourList = tutorTabViewModel.getDutyHours(); - if(dutyHourList == null){ - }else{ - dutyHoursAdapter = new DutyHoursAdapter(getApplicationContext(),dutyHourList); - dutyHoursRecycller.setAdapter(dutyHoursAdapter); - addDutyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - addEmptyDuty(dutyHoursAdapter); - } - }); - } - userRoom.setText(String.format("%s", tutorTabViewModel.getRoom())); - userEmail.setText(String.format("%s", tutorTabViewModel.getEmailTutorTab())); - if (!tutorTabViewModel.getNote().equals("")) { - userNote.setText(String.format("%s", tutorTabViewModel.getNote())); - } - } - - @Override - public void onError(Throwable e) { - int code = ((HttpException) e).response().code(); - if( code == 404){ - ifTutorTabExists = false; - dutyHoursAdapter = new DutyHoursAdapter(getApplicationContext(),new ArrayList()); - dutyHoursRecycller.setAdapter(dutyHoursAdapter); - addDutyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - addEmptyDuty(dutyHoursAdapter); - } - }); - } - showError(e); - } - })); - + getTutorTab(); findViewById(R.id.contentTutorTabInfoImageButton).setOnClickListener(v-> InfoHelperUtils.infoPopUp(v,R.layout.info_popup_tutor_tab)); userName.setText(String.format("%s %s", PrefUtils.getUserFirstName(getApplicationContext()), PrefUtils.getUserLastName(getApplicationContext()))); disposable.add( - userService.getUserById(PrefUtils.getUserId(getApplicationContext())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -153,7 +107,46 @@ public class TutorTab extends AppCompatActivity { } })); setUpSaveListener(saveButon); + scrapButton.setOnClickListener(view -> scrapTutorTab()); } + + private void getTutorTab(){ + disposable.add( + tutorTabService.apiUsersTutorTabByTutorIdGet(PrefUtils.getUserId(getApplicationContext())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(new DisposableSingleObserver() { + @Override + public void onSuccess(TutorTabViewModel tutorTabViewModel) { + + dutyHourList = tutorTabViewModel.getDutyHours(); + if(dutyHourList != null){ + dutyHoursAdapter = new DutyHoursAdapter(getApplicationContext(),dutyHourList); + dutyHoursRecycller.setAdapter(dutyHoursAdapter); + addDutyButton.setOnClickListener(v -> addEmptyDuty(dutyHoursAdapter)); + } + userRoom.setText(String.format("%s", tutorTabViewModel.getRoom())); + userEmail.setText(String.format("%s", tutorTabViewModel.getEmailTutorTab())); + if (!tutorTabViewModel.getNote().equals("")) { + userNote.setText(String.format("%s", tutorTabViewModel.getNote())); + } + } + + @Override + public void onError(Throwable e) { + int code = ((HttpException) e).response().code(); + if( code == 404){ + ifTutorTabExists = false; + dutyHoursAdapter = new DutyHoursAdapter(getApplicationContext(),new ArrayList()); + dutyHoursRecycller.setAdapter(dutyHoursAdapter); + addDutyButton.setOnClickListener(v -> addEmptyDuty(dutyHoursAdapter)); + } + showError(e); + } + })); + + } + private void addEmptyDuty(DutyHoursAdapter adapter){ adapter.addDuty(new DutyHourViewModel()); adapter.notifyItemInserted(adapter.getItemCount()); @@ -161,20 +154,17 @@ public class TutorTab extends AppCompatActivity { } private void setUpSaveListener(Button button) { - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - newTab = new TutorTabViewModel(PrefUtils.getUserId(getApplicationContext()), - userRoom.getText().toString(), - userEmail.getText().toString(), - userNote.getText().toString(), - dutyHoursAdapter.getDutyList()); - if(ifTutorTabExists){ - putUserTab(newTab); - }else{ - postUserTab(newTab); - ifTutorTabExists=true; - } + button.setOnClickListener(view -> { + newTab = new TutorTabViewModel(PrefUtils.getUserId(getApplicationContext()), + userRoom.getText().toString(), + userEmail.getText().toString(), + userNote.getText().toString(), + dutyHoursAdapter.getDutyList()); + if(ifTutorTabExists){ + putUserTab(newTab); + }else{ + postUserTab(newTab); + ifTutorTabExists=true; } }); } @@ -185,23 +175,35 @@ public class TutorTab extends AppCompatActivity { .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::handleResponsePut, this::handleError)); } + private void postUserTab(TutorTabViewModel tutorTabViewModel) { disposable.add(tutorTabService.apiUsersTutorTabByTutorIdPost(PrefUtils.getUserId(getApplicationContext()), tutorTabViewModel) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::handleResponsePost, this::handleError)); } + private void scrapTutorTab() { + + disposable.add(tutorTabService.apiUsersScrapTutorTabByTutorIdPost(PrefUtils.getUserId(getApplicationContext())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::handleResponseScrap, this::handleError)); + + } + + private void handleResponseScrap() { + Toast.makeText(getApplicationContext(), getApplicationContext().getString(R.string.scrap_tutor_msg), Toast.LENGTH_SHORT).show(); + getTutorTab(); + } private void handleResponsePut(Response resp) { Toast.makeText(getApplicationContext(), getApplicationContext().getString(R.string.updateToast), Toast.LENGTH_SHORT).show(); - } + private void handleResponsePost(TutorTabViewModel tutorTabViewModel ) { Toast.makeText(getApplicationContext(), getApplicationContext().getString(R.string.updateToast), Toast.LENGTH_SHORT).show(); - } - private void handleError(Throwable error) { if (error instanceof HttpException) { @@ -212,6 +214,10 @@ public class TutorTab extends AppCompatActivity { } else { Toast.makeText(getApplicationContext(), "Network error " + error.getMessage(), Toast.LENGTH_SHORT).show(); + + + Log.e("WMI SUCC", String.valueOf(error)); + } } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/TutorsListTab.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/TutorsListTab.java new file mode 100644 index 0000000..06e39cc --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/TutorsListTab.java @@ -0,0 +1,46 @@ +package com.uam.wmi.findmytutor.activity; + +//import android.app.Fragment; +import android.support.v4.app.Fragment; +import android.os.Bundle; +import android.support.v4.app.FragmentTabHost; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.uam.wmi.findmytutor.R; + +public class TutorsListTab extends Fragment { + private FragmentTabHost mTabHost; + + public TutorsListTab() { + } + + public static TutorsListTab newInstance() { + return new TutorsListTab(); + } + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + } + + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View rootView = inflater.inflate(R.layout.tutors_list_tabs, container, false); + + mTabHost = rootView.findViewById(android.R.id.tabhost); + mTabHost.setup(getActivity(), getFragmentManager(), R.id.realtabcontent); + + mTabHost.addTab(mTabHost.newTabSpec("fragmentb").setIndicator("Fragment B"), + UsersListFragment.class, null); + mTabHost.addTab(mTabHost.newTabSpec("fragmentc").setIndicator("Fragment C"), + UsersListFragment.class, null); + + + + return rootView; + } +} + diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java index 0ed27fc..47e63a3 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java @@ -9,13 +9,17 @@ import android.support.v7.app.AlertDialog; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; -import android.widget.Toast; import com.annimon.stream.Stream; import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; @@ -28,8 +32,8 @@ import com.uam.wmi.findmytutor.model.User; import com.uam.wmi.findmytutor.network.ApiClient; import com.uam.wmi.findmytutor.service.TutorTabApi; import com.uam.wmi.findmytutor.service.UserService; -import com.uam.wmi.findmytutor.utils.InfoHelperUtils; import com.uam.wmi.findmytutor.utils.MyDividerItemDecoration; +import com.uam.wmi.findmytutor.utils.PrefUtils; import com.uam.wmi.findmytutor.utils.RecyclerTouchListener; import com.uam.wmi.findmytutor.utils.RestApiHelper; @@ -66,6 +70,7 @@ public class UsersListFragment extends Fragment { private List tutorsList = new ArrayList<>(); private List tutorsFiltered = new ArrayList<>(); private Collator plCollator = Collator.getInstance(Locale.forLanguageTag("pl-PL")); + private Boolean fetchOnlyOnlineUsers = PrefUtils.getShowOnlyOnlineUsers(getApplicationContext()); public UsersListFragment() { } @@ -78,7 +83,7 @@ public class UsersListFragment extends Fragment { mAdapter = new TutorsListAdapter(getActivity().getApplicationContext(), tutorsFiltered); View view = inflater.inflate(R.layout.users_list, container, false); view.setBackgroundColor(getResources().getColor(android.R.color.white)); - + setHasOptionsMenu(true); return view; } @@ -94,6 +99,7 @@ public class UsersListFragment extends Fragment { RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(mLayoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); + recyclerView.setVerticalScrollBarEnabled(true); recyclerView.addItemDecoration(new MyDividerItemDecoration(getActivity(), LinearLayoutManager.VERTICAL, 16)); recyclerView.setAdapter(mAdapter); @@ -113,12 +119,50 @@ public class UsersListFragment extends Fragment { } - public void searchUser(String textToSearch) { - tutorsFiltered.clear(); - tutorsFiltered.addAll(Stream.of(tutorsList).filter(t -> - t.toSearchAbleString().toLowerCase().contains(textToSearch.toLowerCase())).toList()); - mAdapter.notifyDataSetChanged(); + + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + try { + menu.findItem(R.id.showOnlineUsersOnly).setChecked(fetchOnlyOnlineUsers); + } + catch(Exception e) { + Log.e(TAG, "onPrepareOptionsMenu error"); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + //item.setChecked(fetchOnlyOnlineUsers); + switch (item.getItemId()){ + case R.id.showOnlineUsersOnly: + + if(item.isChecked()) { + fetchOnlyOnlineUsers = false; + item.setChecked(false); + }else{ + item.setChecked(true); + fetchOnlyOnlineUsers = true; + } + + PrefUtils.putShowOnlyOnlineUsers(getApplicationContext(), fetchOnlyOnlineUsers); + fetchAllTutors(); + break; + } + + return false; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.users_list_menu, menu); + menu.getItem(0).setChecked(fetchOnlyOnlineUsers); + super.onCreateOptionsMenu(menu,inflater); + } + + public void searchUser(String textToSearch) { + searchTutorInBackend(textToSearch); } private void showNoteDialog(final User user) { @@ -185,7 +229,9 @@ public class UsersListFragment extends Fragment { private void fetchAllTutors() { disposable.add( - userService.getAllTutors() + (fetchOnlyOnlineUsers ? + userService.getAllActiveTutors() : + userService.getAllOfflineTutors()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(tutors -> { @@ -228,22 +274,70 @@ public class UsersListFragment extends Fragment { } + private void searchTutorInBackend(String searchString) { + disposable.add( + userService.searchUser(searchString) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(new DisposableSingleObserver>() { + @Override + public void onSuccess(List users) { + tutorsFiltered.clear(); + tutorsFiltered.addAll(users); + mAdapter.notifyDataSetChanged(); + } + + @Override + public void onError(Throwable e) { + showSearchError(e); + } + })); + } + + + public void restoreUsersList(){ + fetchAllTutors(); + } + private int sortByUserName(User t1, User t2) { return plCollator.compare(t1.getLastName(), t2.getLastName()); } private void showError(Throwable e) { String message; + Log.e(TAG, String.valueOf(e)); if (e instanceof HttpException) { ResponseBody responseBody = ((HttpException) e).response().errorBody(); message = RestApiHelper.getErrorMessage(responseBody); } else { - message = "Network Error !"; + message = getString(R.string.network_err); } - Snackbar.make(coordinatorLayout, message, Snackbar.LENGTH_LONG) - .show(); + createSnackbar(message); + } + + private void showSearchError(Throwable e) { + String message; + Log.e(TAG, String.valueOf(e)); + + if (e instanceof HttpException) { + ResponseBody responseBody = ((HttpException) e).response().errorBody(); + message = RestApiHelper.getErrorMessage(responseBody); + } else { + message = getString(R.string.search_null); + } + + createSnackbar(message); + } + + private void createSnackbar(String msg){ + Snackbar snackbar = Snackbar.make(coordinatorLayout, msg, Snackbar.LENGTH_LONG); + View view = snackbar.getView(); + CoordinatorLayout.LayoutParams params=(CoordinatorLayout.LayoutParams)view.getLayoutParams(); + params.gravity = Gravity.TOP; + view.setLayoutParams(params); + snackbar.show(); } private void toggleEmptyNotes() { @@ -251,6 +345,11 @@ public class UsersListFragment extends Fragment { noNotesView.setVisibility(View.GONE); } else { noNotesView.setVisibility(View.VISIBLE); + if(fetchOnlyOnlineUsers) + noNotesView.setText(R.string.no_online_users); + else{ + noNotesView.setText(R.string.no_offline_users); + } } } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/WhiteList.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/WhiteList.java new file mode 100644 index 0000000..d251307 --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/WhiteList.java @@ -0,0 +1,335 @@ +package com.uam.wmi.findmytutor.activity; + +import android.annotation.SuppressLint; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.Switch; +import android.widget.TextView; +import android.widget.Toast; + +import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; +import com.uam.wmi.findmytutor.R; +import com.uam.wmi.findmytutor.adapters.WhiteListAdapter; +import com.uam.wmi.findmytutor.model.IsUsingListBool; +import com.uam.wmi.findmytutor.model.StudentIdModel; +import com.uam.wmi.findmytutor.model.User; +import com.uam.wmi.findmytutor.network.ApiClient; +import com.uam.wmi.findmytutor.service.UserService; +import com.uam.wmi.findmytutor.utils.MyDividerItemDecoration; +import com.uam.wmi.findmytutor.utils.PrefUtils; +import com.uam.wmi.findmytutor.utils.RecyclerTouchListener; +import com.uam.wmi.findmytutor.utils.RestApiHelper; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.observers.DisposableSingleObserver; +import io.reactivex.schedulers.Schedulers; +import okhttp3.ResponseBody; + +public class WhiteList extends AppCompatActivity { + + private CompositeDisposable disposable = new CompositeDisposable(); + private UserService userService; + private boolean didFetched = false; + private String tutorId; + + @BindView(R.id.recycler_view_whitelist) + RecyclerView recyclerView; + @BindView(R.id.white_list_empty_text_view) + TextView noNotesView; + @BindView(R.id.switch_whitelist_toggle) + Switch aSwitch; + @BindView(R.id.add_to_white_list_fab) + FloatingActionButton addToWhiteListFab; + private WhiteListAdapter mAdapter; + private List whitelistedUsers = new ArrayList<>(); + private HashSet whitelistedUsersIDs = new HashSet<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + setContentView(R.layout.activity_white_list); + ButterKnife.bind(this); + tutorId = PrefUtils.getUserId(getApplicationContext()); + userService = ApiClient.getClient(getApplicationContext()) + .create(UserService.class); + + if (PrefUtils.isWhiteListing(this)){ + aSwitch.setText(getString(R.string.action_white_list) +" ON"); + aSwitch.setChecked(true); + handleChangeRequest(true); + }else{ + aSwitch.setText(getString(R.string.action_white_list) +" OFF"); + aSwitch.setChecked(false); + handleChangeRequest(false); + } + + Toolbar toolbar = findViewById(R.id.toolbar); + toolbar.setTitle(getString(R.string.activity_title_whitelist)); + setSupportActionBar(toolbar); + + mAdapter = new WhiteListAdapter(this, whitelistedUsers); + RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); + recyclerView.setLayoutManager(mLayoutManager); + recyclerView.setItemAnimator(new DefaultItemAnimator()); + recyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16)); + recyclerView.setAdapter(mAdapter); + + /** + * On long press on RecyclerView item, open alert dialog + * with options to choose + * Edit and Delete + * */ + recyclerView.addOnItemTouchListener(new RecyclerTouchListener(this, + recyclerView, new RecyclerTouchListener.ClickListener() { + @Override + public void onClick(View view, final int position) { + } + + @Override + public void onLongClick(View view, int position) { + } + })); + + addToWhiteListFab.setOnClickListener(this::showFabDialog); + + fetchWhiteListedUsersIDs(PrefUtils.getUserId(getApplicationContext())); + handleSwitch(); + } + + private void fetchWhiteListedUsersIDs(String userId) { + disposable.add( + userService.getTutorWhitelistedByID(userId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(new DisposableSingleObserver>() { + @Override + public void onSuccess(List users) { + whitelistedUsersIDs.addAll(users); + didFetched = true; + fetchWhiteListedUsers(); + toggleEmptyNotes(); + } + + @Override + public void onError(Throwable e) { + showError(e); + } + }) + ); + } + + private void fetchWhiteListedUsers() { + for (String GUID : whitelistedUsersIDs){ + disposable.add( + userService.getUserById(GUID) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(new DisposableSingleObserver() { + @Override + public void onSuccess(User user) { + whitelistedUsers.add(user); + toggleEmptyNotes(); + + if (whitelistedUsers.size() == whitelistedUsersIDs.size()) { + mAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onError(Throwable e) { + showError(e); + } + }) + ); + } + } + + + private void showFabDialog(View v){ + LayoutInflater layoutInflaterAndroid = LayoutInflater.from(getApplicationContext()); + @SuppressLint("InflateParams") View view = layoutInflaterAndroid.inflate(R.layout.white_list_fab_modal, null); + AlertDialog.Builder alertDialogBuilderUserInput = new AlertDialog.Builder(this); + + alertDialogBuilderUserInput.setView(view).setPositiveButton(getApplicationContext().getString(R.string.modal_location_send), null); + + alertDialogBuilderUserInput + .setPositiveButton(R.string.add, null) + .setNegativeButton(R.string.cancel, null); + + final AlertDialog alertDialog = alertDialogBuilderUserInput.create(); + alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + EditText modalUserInput = view.findViewById(R.id.white_list_modal_input); + + alertDialog.setOnShowListener(dialogInterface -> { + Button sendButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + + Button dismissButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE); + + dismissButton.setOnClickListener(view1 -> alertDialog.dismiss()); + + sendButton.setOnClickListener(view1 -> { + String body = modalUserInput.getText().toString(); + + if (TextUtils.isEmpty(body)) { + Toast.makeText(getApplicationContext(), R.string.can_not_be_empty, Toast.LENGTH_SHORT).show(); + modalUserInput.requestFocus(); + } else { + sendUserToWhitelist(body); + alertDialog.dismiss(); + } + }); + }); + + alertDialog.show(); + } + + private void sendUserToWhitelist(String body) { + StudentIdModel studentIdModel = new StudentIdModel(body); + disposable.add( + userService.addStudentToWhitelist(PrefUtils.getUserId(getApplicationContext()), studentIdModel) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::handleAddUser,this::showError) + ); + } + + private void handleAddUser(User user) { + whitelistedUsersIDs.clear(); + whitelistedUsers.clear(); + whitelistedUsersIDs.addAll(user.getWhitelist()); + fetchWhiteListedUsers(); + } + + private void showError(Throwable e) { + String message; + + if (e instanceof HttpException) { + ResponseBody responseBody = ((HttpException) e).response().errorBody(); + message = RestApiHelper.getErrorMessage(responseBody); + if (((HttpException) e).response().code() == 404) { + message = getString(R.string.no_such_a_user); + } + } else { + message = "Network Error !"; + } + Toast.makeText(this, message, Snackbar.LENGTH_LONG).show(); + } + + private void toggleEmptyNotes() { + + if (didFetched && whitelistedUsers.size() == 0) { + noNotesView.setText(R.string.list_is_empty); + noNotesView.setVisibility(View.VISIBLE); + }else if (whitelistedUsers.size() > 0) { + noNotesView.setVisibility(View.GONE); + } else { + noNotesView.setText(getString(R.string.loading)); + noNotesView.setVisibility(View.VISIBLE); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + disposable.dispose(); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_white_list, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + if (item.getItemId()==R.id.action_whitelist_info_popup){ + int layoutID = R.layout.info_popup_whitelist; + + View popupView = getLayoutInflater().inflate(layoutID,null); + + PopupWindow popupWindow = new PopupWindow(popupView, + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + // If the PopupWindow should be focusable + popupWindow.setFocusable(true); + // If you need the PopupWindow to dismiss when when touched outside + popupWindow.setBackgroundDrawable(new ColorDrawable()); + // Get the View's(the one that was clicked in the Fragment) location + View anchorView= getWindow().getDecorView().findViewById(android.R.id.content); + popupWindow.showAtLocation(anchorView,Gravity.TOP|Gravity.END, 0, 0); + } + + return super.onOptionsItemSelected(item); + } + + private void handleSwitch(){ + aSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (isChecked){ + aSwitch.setText(getString(R.string.action_white_list) + " "+ getString(R.string.on)); + handleChangeRequest(true); + PrefUtils.useWhitelist(this, true); + }else { + aSwitch.setText(getString(R.string.action_white_list) + " "+getString(R.string.off)); + handleChangeRequest(false); + PrefUtils.useWhitelist(this, false); + } + }); + } + + private void handleChangeRequest(boolean value){ + IsUsingListBool isUsingListBool = new IsUsingListBool(); + isUsingListBool.setIsUsing(value); + disposable.add( + userService.setTutorWhitelist(tutorId, isUsingListBool) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(()->{ + },this::showError) + ); + } +} diff --git a/app/src/main/java/com/uam/wmi/findmytutor/adapters/BlackListAdapter.java b/app/src/main/java/com/uam/wmi/findmytutor/adapters/BlackListAdapter.java new file mode 100644 index 0000000..3fd9043 --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/adapters/BlackListAdapter.java @@ -0,0 +1,134 @@ +package com.uam.wmi.findmytutor.adapters; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.design.widget.Snackbar; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.TextView; +import android.widget.Toast; + +import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; +import com.uam.wmi.findmytutor.R; +import com.uam.wmi.findmytutor.model.StudentIdModel; +import com.uam.wmi.findmytutor.model.User; +import com.uam.wmi.findmytutor.network.ApiClient; +import com.uam.wmi.findmytutor.service.UserService; +import com.uam.wmi.findmytutor.utils.PrefUtils; +import com.uam.wmi.findmytutor.utils.RestApiHelper; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; +import okhttp3.ResponseBody; + +import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; + + +public class BlackListAdapter extends RecyclerView.Adapter { + + private Context context; + private List tutorsList; + private CompositeDisposable disposable = new CompositeDisposable(); + private UserService userService; + + + public BlackListAdapter(Context context, List tutors) { + this.context = context; + this.tutorsList = tutors; + } + @NonNull + @Override + public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + userService = ApiClient.getClient(getApplicationContext()) + .create(UserService.class); + View itemView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.black_list_row, parent, false); + + return new MyViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { + Drawable image = null; + User tutor = tutorsList.get(position); + + holder.firstName.setText(tutor.getFirstName() + " " + tutor.getLastName()); + holder.lastName.setText("Index: " + tutor.getLdapLogin() + " Email: " + tutor.getEmail()); +//"s416196" + holder.imageButton.setOnClickListener(l ->{ + StudentIdModel studentIdModel = new StudentIdModel(tutor.getLdapLogin()); + String tutorId = PrefUtils.getUserId(getApplicationContext()); + disposable.add( + userService.removeStudentFromBlacklist(tutorId, studentIdModel) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(()->{ + Toast.makeText(getApplicationContext(), "User removed", Toast.LENGTH_SHORT).show(); + tutorsList.remove(position); + notifyDataSetChanged(); + },this::showError) + ); + }); +// +// if (tutor.isIsOnline()) { +// image = context.getResources().getDrawable(R.drawable.user_list_online); +// } else { +// image = context.getResources().getDrawable(R.drawable.user_list_offline); +// } +// +// if (!tutor.isIsActive()) { +// image = context.getResources().getDrawable(R.drawable.user_list_off); +// } + +// image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); +// holder.isOnline.setCompoundDrawables(image, null, null, null); + } + + @Override + public int getItemCount() { + return tutorsList.size(); + } + + class MyViewHolder extends RecyclerView.ViewHolder { + + @BindView(R.id.firstName) + TextView firstName; + + @BindView(R.id.lastName) + TextView lastName; + +// @BindView(R.id.isOnline) +// TextView isOnline; + @BindView(R.id.removeUserImageButton) + ImageButton imageButton; + + MyViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + } + } + + private void showError(Throwable e) { + String message; + + if (e instanceof HttpException) { + ResponseBody responseBody = ((HttpException) e).response().errorBody(); + message = RestApiHelper.getErrorMessage(responseBody); + } else { + message = "Network Error !"; + } + Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/uam/wmi/findmytutor/adapters/TutorsListAdapter.java b/app/src/main/java/com/uam/wmi/findmytutor/adapters/TutorsListAdapter.java index 87210e5..ed2f50a 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/adapters/TutorsListAdapter.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/adapters/TutorsListAdapter.java @@ -37,7 +37,7 @@ public class TutorsListAdapter extends RecyclerView.Adapter { + + private Context context; + private List tutorsList; + private CompositeDisposable disposable = new CompositeDisposable(); + private UserService userService; + + + public WhiteListAdapter(Context context, List tutors) { + this.context = context; + this.tutorsList = tutors; + } + @NonNull + @Override + public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + userService = ApiClient.getClient(getApplicationContext()) + .create(UserService.class); + View itemView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.white_list_row, parent, false); + + return new MyViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { + Drawable image = null; + User tutor = tutorsList.get(position); + + holder.firstName.setText(tutor.getFirstName() + " " + tutor.getLastName()); + holder.lastName.setText("Index: " + tutor.getLdapLogin() + " Email: " + tutor.getEmail()); +//"s416196" + holder.imageButton.setOnClickListener(l ->{ + StudentIdModel studentIdModel = new StudentIdModel(tutor.getLdapLogin()); + String tutorId = PrefUtils.getUserId(getApplicationContext()); + disposable.add( + userService.removeStudentFromWhitelist(tutorId, studentIdModel) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(()->{ + Toast.makeText(getApplicationContext(), "User removed", Toast.LENGTH_SHORT).show(); + tutorsList.remove(position); + notifyDataSetChanged(); + },this::showError) + ); + }); +// +// if (tutor.isIsOnline()) { +// image = context.getResources().getDrawable(R.drawable.user_list_online); +// } else { +// image = context.getResources().getDrawable(R.drawable.user_list_offline); +// } +// +// if (!tutor.isIsActive()) { +// image = context.getResources().getDrawable(R.drawable.user_list_off); +// } + +// image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); +// holder.isOnline.setCompoundDrawables(image, null, null, null); + } + + @Override + public int getItemCount() { + return tutorsList.size(); + } + + class MyViewHolder extends RecyclerView.ViewHolder { + + @BindView(R.id.firstName) + TextView firstName; + + @BindView(R.id.lastName) + TextView lastName; + +// @BindView(R.id.isOnline) +// TextView isOnline; + @BindView(R.id.removeUserImageButton) + ImageButton imageButton; + + MyViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + } + } + + private void showError(Throwable e) { + String message; + + if (e instanceof HttpException) { + ResponseBody responseBody = ((HttpException) e).response().errorBody(); + message = RestApiHelper.getErrorMessage(responseBody); + } else { + message = "Network Error !"; + } + Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/uam/wmi/findmytutor/model/StudentIdModel.java b/app/src/main/java/com/uam/wmi/findmytutor/model/StudentIdModel.java index 3eea407..ab1f37c 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/model/StudentIdModel.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/model/StudentIdModel.java @@ -18,6 +18,15 @@ public class StudentIdModel extends BaseResponse{ @SerializedName("ldapLogin") private String ldapLogin = null; + /** + * + * @param str + */ + public StudentIdModel(String str){ +// this.studentId = str; + this.ldapLogin = str; + } + public StudentIdModel studentId(String studentId) { this.studentId = studentId; return this; diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java index 3d3bdef..c05680c 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java @@ -11,12 +11,13 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.location.Location; +import android.location.LocationListener; import android.location.LocationManager; import android.os.AsyncTask; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.support.v4.app.NotificationCompat; @@ -24,8 +25,10 @@ import android.util.Log; import com.annimon.stream.Stream; import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; import com.google.android.gms.location.LocationServices; -import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.geometry.LatLng; import com.uam.wmi.findmytutor.model.Coordinate; @@ -33,21 +36,17 @@ import com.uam.wmi.findmytutor.network.ApiClient; import com.uam.wmi.findmytutor.utils.ApproximatedLocalization; import com.uam.wmi.findmytutor.utils.MapUtils; import com.uam.wmi.findmytutor.utils.PrefUtils; -import com.uam.wmi.findmytutor.utils.RestApiHelper; import com.uam.wmi.findmytutor.utils.SharingLevel; import org.apache.commons.collections4.queue.CircularFifoQueue; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.observers.DisposableCompletableObserver; -import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.schedulers.Schedulers; -import okhttp3.ResponseBody; import static com.uam.wmi.findmytutor.utils.Const.offlineBackgroundLocationInterval; import static com.uam.wmi.findmytutor.utils.Const.onlineBackgroundLocationInterval; @@ -60,32 +59,33 @@ import static java.lang.String.valueOf; public class BackgroundLocalizationService extends Service { private static final String TAG = "MyLocationService"; - private static final float LOCATION_DISTANCE = 1f; - private static long notify_interval = onlineBackgroundLocationInterval; - private static long notify_interval_inside_building = onlineBackgroundLocationInterval; - private static long notify_interval_outside_building = offlineBackgroundLocationInterval; + private static Integer notify_interval = onlineBackgroundLocationInterval; + private static Integer notify_interval_inside_building = onlineBackgroundLocationInterval; + private static Integer notify_interval_outside_building = offlineBackgroundLocationInterval; private static int coordinatesHistoryLength = 5; - private static final Long LOCATION_INTERVAL = notify_interval; + private Boolean highAccuracyMode; private Location mLastLocation; private Boolean stopService = false; private ArrayList providers = new ArrayList(); - private LocationListener[] mLocationListeners; private CircularFifoQueue coordinatesHistory = new CircularFifoQueue(coordinatesHistoryLength); - private LocationManager mLocationManager = null; private Handler mHandler = new Handler(); private Runnable mStatusChecker; private FusedLocationProviderClient mFusedLocationClient; + private Location fakeLoc = null; + private LocationRequest mLocationRequest; + private Location mCurrentLocation; + private LocationCallback mLocationCallback; + private LocationListener mLocationListener; public BackgroundLocalizationService() { providers.add(LocationManager.GPS_PROVIDER); providers.add(LocationManager.NETWORK_PROVIDER); providers.add(LocationManager.PASSIVE_PROVIDER); + fakeLoc = new Location(""); - mLocationListeners = new LocationListener[]{ - new LocationListener(LocationManager.GPS_PROVIDER), - new LocationListener(LocationManager.NETWORK_PROVIDER), - new LocationListener(LocationManager.PASSIVE_PROVIDER) - }; + fakeLoc.setLatitude(0); + fakeLoc.setLongitude(0); + fakeLoc.setAltitude(0); } @Override @@ -100,7 +100,9 @@ public class BackgroundLocalizationService extends Service { if (intent != null) { stopService = intent.getBooleanExtra("request_stop", false); + notify_interval = intent.getIntExtra("notify_interval", onlineBackgroundLocationInterval); } + if (stopService) { storeBackgroundLocationStatus(getApplication(), false); stopForeground(true); @@ -111,6 +113,20 @@ public class BackgroundLocalizationService extends Service { return START_STICKY; } + private void createLocationCallback() { + mLocationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + super.onLocationResult(locationResult); + if (locationResult != null) { + mCurrentLocation = locationResult.getLastLocation(); + sendCoordinateToBackend(mCurrentLocation); + changeBackgroundMode(); + } + } + }; + } + @Override public void onCreate() { Log.e(TAG, "onCreate"); @@ -124,35 +140,13 @@ public class BackgroundLocalizationService extends Service { startForeground(1001, notification); } - mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); - - initializeLocationManager(); - - Integer providerIndex = 0; - - for (LocationListener listener : mLocationListeners) { - try { - mLocationManager.requestLocationUpdates( - providers.get(providerIndex), - LOCATION_INTERVAL, - LOCATION_DISTANCE, - listener - ); - - } catch (java.lang.SecurityException ex) { - Log.i(TAG, "fail to request location update, ignore", ex); - } catch (IllegalArgumentException ex) { - Log.d(TAG, "network provider does not exist, " + ex.getMessage()); - } - - providerIndex++; - } - - if (!stopService) { + if (!stopService && !PrefUtils.getLocationLevel(getApplicationContext()).equals(SharingLevel.MANUAL.toString())) { + createFusedLocationClient(); + } else if (!stopService && + PrefUtils.getLocationLevel(getApplicationContext()).equals(SharingLevel.MANUAL.toString())) { mStatusChecker = () -> { try { - getLocalizationFromListeners(); - changeBackgroundMode(); + sendCoordinateToBackend(fakeLoc); } finally { mHandler.postDelayed(mStatusChecker, notify_interval); } @@ -162,20 +156,54 @@ public class BackgroundLocalizationService extends Service { } } - private void changeBackgroundMode() { - if (coordinatesHistory.size() > 4) { - Boolean shouldExtendTimeInterval = Stream.of(coordinatesHistory) - .map(MapUtils::checkIfCoordinateIsValid).takeWhile(s -> !s).toList().size() == coordinatesHistory.size(); + private void createFusedLocationClient() { + Integer saveMode = Long.valueOf(notify_interval).compareTo(Long.valueOf(offlineBackgroundLocationInterval)); - Boolean shouldAbbreviateTimeInterval = Stream.of(coordinatesHistory). - map(MapUtils::checkIfCoordinateIsValid).toList().get(coordinatesHistory.size() - 1); - - if (shouldExtendTimeInterval) { - notify_interval = notify_interval_outside_building; - } else if (shouldAbbreviateTimeInterval) { - notify_interval = notify_interval_inside_building; - } + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + return; } + + mLocationRequest = new LocationRequest(); + + if (saveMode != 0) { + mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + } else { + mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); + } + + mLocationRequest.setFastestInterval(notify_interval); + mLocationRequest.setInterval(notify_interval); + + createLocationCallback(); + + if (!stopService) { + mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); + mFusedLocationClient.requestLocationUpdates(mLocationRequest, + mLocationCallback, Looper.getMainLooper()); + } + } + + private void changeBackgroundMode() { + Integer prevInterval = notify_interval; + Boolean shouldExtendTimeInterval = Stream.of(coordinatesHistory) + .map(MapUtils::checkIfCoordinateIsValid).takeWhile(s -> !s).toList().size() == coordinatesHistory.size(); + + Boolean shouldAbbreviateTimeInterval = Stream.of(coordinatesHistory). + map(MapUtils::checkIfCoordinateIsValid).filter(x -> x).toList().size() > 0; + + if (shouldAbbreviateTimeInterval) { + notify_interval = notify_interval_inside_building; + } + + if (coordinatesHistory.size() > 4 && shouldExtendTimeInterval) { + notify_interval = notify_interval_outside_building; + } + + Integer changedMode = Long.valueOf(prevInterval).compareTo(Long.valueOf(notify_interval)); + if (changedMode != 0) { + updateListeners(); + } + } @RequiresApi(api = Build.VERSION_CODES.O) @@ -198,50 +226,6 @@ public class BackgroundLocalizationService extends Service { startForeground(2, notification); } - private void getLocalizationFromListeners() { - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - // TODO: Consider calling - // ActivityCompat#requestPermissions - // here to request the missing permissions, and then overriding - // public void onRequestPermissionsResult(int requestCode, String[] permissions, - // int[] grantResults) - // to handle the case where the user grants the permission. See the documentation - // for ActivityCompat#requestPermissions for more details. - return; - } - - List providers1 = mLocationManager.getProviders(true); - Location bestLocation = null; - AtomicReference triggerAnotherLocationListener = new AtomicReference<>(false); - - mFusedLocationClient.getLastLocation().addOnSuccessListener( - location -> { - if (location != null) { - mLastLocation = location; - coordinatesHistory.add(location); - sendCoordinateToBackend(location); - } - - triggerAnotherLocationListener.set(true); - }); - - if (triggerAnotherLocationListener.get()) { - for (String provider : providers1) { - Location location = mLocationManager.getLastKnownLocation(provider); - - if (location == null) { - continue; - } - if (bestLocation == null || location.getAccuracy() < bestLocation.getAccuracy()) { - bestLocation = location; - } - } - - coordinatesHistory.add(bestLocation); - } - - } - private void sendCoordinateToBackend(Location location) { new Task(location).execute(); } @@ -252,59 +236,49 @@ public class BackgroundLocalizationService extends Service { super.onDestroy(); mHandler.removeCallbacks(mStatusChecker); + destroyLocationListeners(); + } - if (mLocationManager != null) { - for (LocationListener listener : mLocationListeners) { - try { - if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - return; - } - mLocationManager.removeUpdates(listener); - - } catch (Exception ex) { - Log.i(TAG, "fail to remove location listener, ignore", ex); - } - } + private void destroyLocationListeners() { + if (mFusedLocationClient != null) { + mFusedLocationClient.removeLocationUpdates(mLocationCallback); + mFusedLocationClient = null; + mLocationCallback = null; + mLocationRequest = null; } } - private void initializeLocationManager() { - Log.e(TAG, "initializeLocationManager - LOCATION_INTERVAL: " + LOCATION_INTERVAL + " LOCATION_DISTANCE: " + LOCATION_DISTANCE); - - if (mLocationManager == null) { - mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE); + private void updateListeners() { + if (mFusedLocationClient != null) { + mFusedLocationClient.removeLocationUpdates(mLocationCallback) + .addOnCompleteListener(task -> { + restartService(); + mFusedLocationClient = null; + mLocationCallback = null; + mLocationRequest = null; + }); } } - - private class LocationListener implements android.location.LocationListener { - LocationListener(String provider) { - Log.e(TAG, "LocationListener " + provider); - mLastLocation = new Location(provider); + private void restartService() { + + Intent stopIntent = new Intent(getApplicationContext(), BackgroundLocalizationService.class); + stopIntent.putExtra("request_stop", true); + + stopService(stopIntent); + + Intent startIntent = new Intent(getApplicationContext(), BackgroundLocalizationService.class); + startIntent.putExtra("notify_interval", notify_interval); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { + startForegroundService(startIntent); + } else { + startService(startIntent); } - @Override - public void onLocationChanged(Location location) { - Log.e(TAG, "onLocationChanged: " + location); - mLastLocation.set(location); - } - - @Override - public void onProviderDisabled(String provider) { - Log.e(TAG, "onProviderDisabled: " + provider); - } - - @Override - public void onProviderEnabled(String provider) { - Log.e(TAG, "onProviderEnabled: " + provider); - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - Log.e(TAG, "onStatusChanged: " + provider); - } } + @SuppressLint("StaticFieldLeak") private class Task extends AsyncTask { private ApproximatedLocalization approximatedLocalization; @@ -331,6 +305,10 @@ public class BackgroundLocalizationService extends Service { String status = (PrefUtils.isStatusEnabled(getApplicationContext())) ? PrefUtils.getUserStatus(getApplicationContext()) : ""; if (locationLevel.equals(SharingLevel.PRESENCE.toString())) { + if (!MapUtils.checkIfCoordinateIsValid(latitude, longitude)) { + return null; + } + latitude = presenceLatitude; longitude = presenceLongitude; approximatedBuildingPart = presenceApproximatedName; @@ -345,6 +323,12 @@ public class BackgroundLocalizationService extends Service { approximatedBuildingPart = PrefUtils.getManualLocationApproximation(getApplicationContext()); } + Location fakeLoc = new Location(""); + + fakeLoc.setLatitude(latitude); + fakeLoc.setLongitude(longitude); + coordinatesHistory.add(fakeLoc); + try { Coordinate coordinate = new Coordinate( latitude, @@ -362,7 +346,6 @@ public class BackgroundLocalizationService extends Service { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { - @Override public void onComplete() { Log.e(TAG, "CoordinateSuccess"); @@ -370,9 +353,7 @@ public class BackgroundLocalizationService extends Service { @Override public void onError(Throwable e) { - - Log.e(TAG,"onErr" + valueOf(e)); - + Log.e(TAG, "onErr" + valueOf(e)); } })); } catch (IllegalArgumentException e) { diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/TutorTabApi.java b/app/src/main/java/com/uam/wmi/findmytutor/service/TutorTabApi.java index 837e356..38b2349 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/service/TutorTabApi.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/service/TutorTabApi.java @@ -3,6 +3,7 @@ package com.uam.wmi.findmytutor.service; import com.uam.wmi.findmytutor.model.TutorTabViewModel; +import io.reactivex.Completable; import io.reactivex.Observable; import io.reactivex.Single; import retrofit2.Response; @@ -17,7 +18,7 @@ public interface TutorTabApi { * @return Call<Void> */ @POST("api/users/scrapTutorTab/{tutorId}") - Observable apiUsersScrapTutorTabByTutorIdPost( + Completable apiUsersScrapTutorTabByTutorIdPost( @retrofit2.http.Path("tutorId") String tutorId ); @@ -27,7 +28,7 @@ public interface TutorTabApi { * @return Call<Void> */ @POST("api/users/scrapTutorTab") - Observable apiUsersScrapTutorTabPost(); + Single apiUsersScrapTutorTabPost(); /** diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/UserService.java b/app/src/main/java/com/uam/wmi/findmytutor/service/UserService.java index 75671ed..6fb51d6 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/service/UserService.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/service/UserService.java @@ -15,9 +15,11 @@ import retrofit2.Response; import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.GET; +import retrofit2.http.HTTP; import retrofit2.http.POST; import retrofit2.http.PUT; import retrofit2.http.Path; +import retrofit2.http.Query; public interface UserService { @@ -30,6 +32,18 @@ public interface UserService { @GET("api/users/tutors") Single > getAllTutors(); + @GET("api/users/tutors/online") + Single > getAllOnlineTutors(); + + @GET("api/users/tutors/active") + Single > getAllActiveTutors(); + + @GET("api/users/tutors/offline") + Single > getAllOfflineTutors(); + + @GET("api/users/tutors/search") + Single > searchUser(@Query(value = "searchString", encoded = true) String searchString); + @POST("api/users") Completable createUser(@Body User user); @@ -61,26 +75,28 @@ public interface UserService { Completable setUserInActive(@Path("userID") String userID); @GET("api/users/blacklist/{tutorID}") - Single> getTutorBlacklistedByID(@Path("tutorID") String tutorID); + Single> getTutorBlacklistedByID(@Path("tutorID") String tutorID); @PUT("api/users/blacklist/{tutorID}") Completable setTutorBlacklist(@Path("tutorID") String tutorID, @Body IsUsingListBool isUsing); @POST("api/users/blacklist/{tutorID}") - Completable addStudentToBlacklist(@Path("tutorID") String tutorID, @Body StudentIdModel student); + Observable addStudentToBlacklist(@Path("tutorID") String tutorID, @Body StudentIdModel student); - @DELETE("api/users/blacklist/{tutorID}") +// @DELETE("api/users/blacklist/{tutorID}") + @HTTP(method = "DELETE", path = "api/users/blacklist/{tutorID}", hasBody = true) Completable removeStudentFromBlacklist(@Path("tutorID") String tutorID, @Body StudentIdModel student); @GET("api/users/whitelist/{tutorID}") - Single> getTutorwhitelistedByID(@Path("tutorID") String tutorID); + Single> getTutorWhitelistedByID(@Path("tutorID") String tutorID); @PUT("api/users/whitelist/{tutorID}") Completable setTutorWhitelist(@Path("tutorID") String tutorID, @Body IsUsingListBool isUsing); @POST("api/users/whitelist/{tutorID}") - Completable addStudentTowhitelist(@Path("tutorID") String tutorID, @Body StudentIdModel student); + Observable addStudentToWhitelist(@Path("tutorID") String tutorID, @Body StudentIdModel student); - @DELETE("api/users/whitelist/{tutorID}") +// @DELETE("api/users/whitelist/{tutorID}") + @HTTP(method = "DELETE", path = "api/users/whitelist/{tutorID}", hasBody = true) Completable removeStudentFromWhitelist(@Path("tutorID") String tutorID, @Body StudentIdModel student); } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/Const.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/Const.java index 48fd35f..0590a2f 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/Const.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/Const.java @@ -8,7 +8,7 @@ import java.util.List; public class Const { public final static String BASE_URL = "https://s416084.projektstudencki.pl/master/"; public final static Integer onlineBackgroundLocationInterval = 7000; - public final static Integer offlineBackgroundLocationInterval = 36000; + public final static Integer offlineBackgroundLocationInterval = 360000; public final static Integer defaultMapZoom = 17; public final static Integer searchMapZoom = 13; public final static Double presenceLatitude = 52.466365; diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/MapUtils.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/MapUtils.java index 3164e2b..cabb845 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/MapUtils.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/MapUtils.java @@ -77,6 +77,10 @@ public class MapUtils { return outsideLatitudeRange.contains(coordinate.getLatitude()) && outsideLongitudeRange.contains(coordinate.getLongitude()); } + public static Boolean checkIfCoordinateIsValid(Double latitude, Double longitude){ + return Const.buildingLatitudeRange.contains(latitude) && Const.buildingLongitudeRange.contains(longitude); + } + // Function for marker animation public static class LatLngEvaluator implements TypeEvaluator { // Method is used to interpolate the marker animation. diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/PrefUtils.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/PrefUtils.java index e41f744..2ae7c57 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/PrefUtils.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/PrefUtils.java @@ -212,4 +212,35 @@ public class PrefUtils { return getSharedPreferences(context).getString("current_manual_location_name", null); } + public static void putShowOnlyOnlineUsers(Context context, Boolean flag) { + SharedPreferences.Editor editor = getSharedPreferences(context).edit(); + editor.putBoolean("show_only_online_users_in_list", flag); + editor.apply(); + } + + public static Boolean getShowOnlyOnlineUsers(Context context) { + return getSharedPreferences(context).getBoolean("show_only_online_users_in_list", true); + } + + public static void useBlacklist(Context context, Boolean flag) { + SharedPreferences.Editor editor = getSharedPreferences(context).edit(); + editor.putBoolean("blacklisting", flag); + editor.putBoolean("whitelisting", false); + editor.apply(); + } + + public static void useWhitelist(Context context, Boolean flag) { + SharedPreferences.Editor editor = getSharedPreferences(context).edit(); + editor.putBoolean("whitelisting", flag); + editor.putBoolean("blacklisting", false); + editor.apply(); + } + + public static Boolean isBlackListing(Context context) { + return getSharedPreferences(context).getBoolean("blacklisting", false); + } + public static Boolean isWhiteListing(Context context) { + return getSharedPreferences(context).getBoolean("whitelisting", false); + } + } \ No newline at end of file diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java index 33d4aee..5d0d5b4 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java @@ -21,6 +21,7 @@ public class RxSearchObservable { @Override public boolean onQueryTextSubmit(String s) { subject.onNext(s); + searchView.clearFocus(); return false; } @@ -31,6 +32,8 @@ public class RxSearchObservable { } + + }); diff --git a/app/src/main/res/drawable/ic_add_person.xml b/app/src/main/res/drawable/ic_add_person.xml new file mode 100644 index 0000000..cc6dd3f --- /dev/null +++ b/app/src/main/res/drawable/ic_add_person.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_black_list.xml b/app/src/main/res/layout/activity_black_list.xml new file mode 100644 index 0000000..b4c9188 --- /dev/null +++ b/app/src/main/res/layout/activity_black_list.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_white_list.xml b/app/src/main/res/layout/activity_white_list.xml new file mode 100644 index 0000000..7c196c5 --- /dev/null +++ b/app/src/main/res/layout/activity_white_list.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/black_list_fab_modal.xml b/app/src/main/res/layout/black_list_fab_modal.xml new file mode 100644 index 0000000..5d49f4f --- /dev/null +++ b/app/src/main/res/layout/black_list_fab_modal.xml @@ -0,0 +1,42 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/black_list_row.xml b/app/src/main/res/layout/black_list_row.xml new file mode 100644 index 0000000..ded676c --- /dev/null +++ b/app/src/main/res/layout/black_list_row.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_black_list.xml b/app/src/main/res/layout/content_black_list.xml new file mode 100644 index 0000000..e7cc148 --- /dev/null +++ b/app/src/main/res/layout/content_black_list.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/content_tutor_tab.xml b/app/src/main/res/layout/content_tutor_tab.xml index ef92cd3..3862c1f 100644 --- a/app/src/main/res/layout/content_tutor_tab.xml +++ b/app/src/main/res/layout/content_tutor_tab.xml @@ -7,6 +7,8 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + +