Enhancing Network Requests by Chaining or Zipping with RxJava

In Eventyay Attendee, making HTTP requests to fetch data from the API is one of the most basic techniques used. RxJava comes in as a great method to help us making asynchronous requests and optimize the code a lot. This blog post will deliver some advanced RxJava used in Eventyay Attendee. Why using RxJava?Advanced RxJava Technique – Chaining network calls with RxJavaAdvanced RxJava Technique – Merging network calls with RxJavaConclusionsResources WHY USING RXJAVA? There are many reasons why RxJava is a great API in Android Development. RxJava is an elegant solution to control data flow in programming, where developers can cache data, get data, update the UI after getting the data, handle asynchronous tasks. RxJava also works really well with MVVM architectural pattern. CHAINING NETWORK CALLS WITH RXJAVA Chaining RxJava is a technique using flatMap() operator of Rxjava. It will use the result from one network call in order to make the next network call.  In Eventyay Attendee, this technique is used when we want to update the user profile image. First, we need to upload the new profile image to the server in order to get the image URL, and then we use that URL to update the user profile compositeDisposable += authService.uploadImage(UploadImage(encodedImage)).flatMap { authService.updateUser(user.copy(avatarUrl = it.url)) }.withDefaultSchedulers() .doOnSubscribe { mutableProgress.value = true } .doFinally { mutableProgress.value = false } .subscribe({ mutableMessage.value = resource.getString(R.string.user_update_success_message) Timber.d("User updated") }) { mutableMessage.value = resource.getString(R.string.user_update_error_message) Timber.e(it, "Error updating user!") } In conclusion, zipping RxJava helps to make HTTP requests more continuous and reduce unnecessary codes.  ZIPPING NETWORK CALLS WITH RXJAVA Zipping RxJava is a technique using zip() operator of Rxjava. It will wait for items from two or more Observables to arrive and then merge them together for emitting. This technique would be useful when two observables emit the same type of data. In Eventyay Attendee, this technique is used when fetching similar events by merging events in the same location and merging events in the same event type. var similarEventsFlowable = eventService.getEventsByLocationPaged(location, requestedPage, 3) if (topicId != -1L) { similarEventsFlowable = similarEventsFlowable .zipWith(eventService.getSimilarEventsPaged(topicId, requestedPage, 3), BiFunction { firstList: List<Event>, secondList: List<Event> -> val similarList = mutableSetOf<Event>() similarList.addAll(firstList + secondList) similarList.toList() }) } compositeDisposable += similarEventsFlowable .take(1) .withDefaultSchedulers() .subscribe({ response -> ... }, { error -> ... }) In conclusion, zipping RxJava helps running all the tasks in parallel and return all of the results in a single callback. CONCLUSION Even though RxJava is pretty hard to understand and master, it is a really powerful tool in Android Development and MVVM models. These techniques above are really simple to implement and they could improve the app by r RESOURCES Eventyay Attendee Source Code:  https://github.com/fossasia/open-event-attendee-android/pull/2010 https://github.com/fossasia/open-event-attendee-android/pull/2117 RxJava Documentation: http://reactivex.io/documentation

Continue ReadingEnhancing Network Requests by Chaining or Zipping with RxJava

Swipe to Check In/Out in Open Event Organizer App

Open Event Organizer App didn’t provide any option for the Event Organizer to view the list of Attendees present under an Order and check them in/out the event. Therefore, we designed a system such that the Organizer can just swipe the attendee present under an order to check them in or out. In this blog post, I will discuss how we implemented this functionality in Open Event Organizer App without using any third party libraries. Specifications We will create a separate class SwipeController.java which extends ItemTouchHelper.SimpleCallback and provide the swiping functionalities to our plain old recyclerview. We will call the super constructor with ItemTouchHelper.LEFT and ItemTouchHelper.RIGHT as arguments to provide left as well as right movements in each recyclerview list item. The bitmaps and paint object initialized here will be used later in onDraw. public SwipeController(OrderDetailViewModel orderDetailViewModel, OrderAttendeesAdapter orderAttendeesAdapter, Context context) { super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT); this.orderDetailViewModel = orderDetailViewModel; this.orderAttendeesAdapter = orderAttendeesAdapter; closeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.close); doneIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.done); paintGreen.setColor(context.getResources().getColor(R.color.light_green_500)); paintRed.setColor(context.getResources().getColor(R.color.red_500)); } Next, we will override getMovementFlags method. This method decides the allowed movement directions for each recyclerview item. The deciding logic is that, if an attendee is checked in then the allowed movement is left to check out and if an attendee is checked out then the allowed movement is right to check in. If neither of the above case, then both movements are allowed. @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = 0; If (orderDetailViewModel.getCheckedInStatus( viewHolder.getAdapterPosition()) == null) makeMovementFlags(dragFlags, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT); if (orderDetailViewModel.getCheckedInStatus( viewHolder.getAdapterPosition())) { return makeMovementFlags(dragFlags, ItemTouchHelper.LEFT); } else { return makeMovementFlags(dragFlags, ItemTouchHelper.RIGHT); } } The onChildDraw method involves the code doing actual drawing. The variables used in code are discussed below. ActionState - Checks the state of the recycler view item. We proceed with the below logic if the item is being swiped. dX - The distance by which the item is swiped. Positive for left and negative for right. Background - Background of the viewholder. Rectangular in shape and dimensions changed with change in dX. IconDest - Calculates the position where the icons (close icon or done icon) is placed in canvas Canvas - Java Canvas on which the drawing is done. We set the background and draw the bitmaps on their location in canvas. @Override public void onChildDraw(Canvas canvas, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { View itemView = viewHolder.itemView; float height = (float) itemView.getBottom() - (float) itemView.getTop(); float width = height / 3; RectF background; Paint paint; Bitmap icon; RectF iconDest; if (dX > 0) { background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom()); paint = paintGreen; icon = doneIcon; iconDest = new RectF((float) itemView.getLeft() + width, (float) itemView.getTop() + width, (float) itemView.getLeft() + 2 * width, (float) itemView.getBottom() - width); } else { background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom()); paint = paintRed; icon = closeIcon; iconDest = new RectF((float) itemView.getRight() - 2 * width, (float) itemView.getTop() +…

Continue ReadingSwipe to Check In/Out in Open Event Organizer App

Share Events in the Open Event Organizer Android App

In the Open Event Organizer Android App, after creating an event the organizer was unable to share it. We handled this challenge and came up with options to share Event with other social media apps. Along with that user can send Email to users containing event description with just a click. All this through a UI that our user will love interacting with. Let’s see how we implemented this. Specifications We designed a UI given below which offer four functionalities to the user in a single screen. The Event Name and Date are shown in the CardView. User can copy Event External URL by clicking on Copy URL option. User can send Email containing information about the Event like ( Name, Description, Starting and Ending Date-Time for the Event) via Email by clicking on Email option. User can share the same information described in point three via other social media/chatting apps etc by clicking on many more option. The is the Event Model class ia a POJO containing the associated attributes. We will use Retrofit to fetch Event object from server through a GET request in EventApi class. @GET("events/{id}?include=tickets") Observable<Event> getEvent(@Path("id") long id); Then we will use the getEvent method of EventRepositoryImpl class to make the request for us using EventApi class and then pass on the Response Event object to the ViewModel by wrapping it in RxJava Observable. We are accepting a boolean field named reload which decdes whether we need to reuse the existing Event object from Local Database  or fetch a new object from server. @Override public Observable<Event> getEvent(long eventId, boolean reload) { Observable<Event> diskObservable = Observable.defer(() -> repository .getItems(Event.class, Event_Table.id.eq(eventId)) .filter(Event::isComplete) .take(1) ); Observable<Event> networkObservable = Observable.defer(() -> eventApi .getEvent(eventId) .doOnNext(this::saveEvent)); return repository.observableOf(Event.class) .reload(reload) .withDiskObservable(diskObservable) .withNetworkObservable(networkObservable) .build(); } In ShareEventVewModel, we are calling the getEvent from EventRepositoryImpl class, construct a LiveData object from it so that UI could observe changes on it. Methods getShareableInformation, getShareableUrl, getShareableSubject provide the shareable information to the UI which is further shared with other apps. public class ShareEventViewModel extends ViewModel { protected LiveData<Event> getEvent(long eventId, boolean reload) { if (eventLiveData.getValue() != null && !reload) return eventLiveData; compositeDisposable.add(eventRepository.getEvent(eventId, reload) .doOnSubscribe(disposable -> progress.setValue(true)) .doFinally(() -> progress.setValue(false)) .subscribe(event -> { this.event = event; eventLiveData.setValue(event); }, throwable -> error.setValue(ErrorUtils.getMessage(throwable)))); return eventLiveData; } public String getShareableInformation() { return Utils.getShareableInformation(event); } } In ShareEventFragment class does the work of binding the UI to the model using data binding. It observes the LiveData objects supplied by presenter and reflect the changes in UI to the LiveData object. public class ShareEventFragment extends BaseFragment implements ShareEventView { public void shareEvent() { Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_TEXT, shareEventViewModel.getShareableInformation()); shareIntent.setType("text/plain"); startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to))); } public void shareByEmail() { Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SENDTO); shareIntent.setType("message/rfc822"); shareIntent.setData(Uri.parse("mailto:")); shareIntent.putExtra(Intent.EXTRA_SUBJECT, shareEventViewModel.getEmailSubject()); shareIntent.putExtra(Intent.EXTRA_TEXT, shareEventViewModel.getShareableInformation()); try { startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to))); } catch (android.content.ActivityNotFoundException ex) { ViewUtils.showSnackbar(binding.getRoot(), "There are no email clients installed"); } } public void copyUrlToClipboard() { ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); String eventUrl = shareEventViewModel.getShareableUrl(); if (eventUrl == null) { ViewUtils.showSnackbar(binding.getRoot(), "Event does not have…

Continue ReadingShare Events in the Open Event Organizer Android App

Change Password Feature for Open Event Android Organizer App

In Open Event Organizer Android App, the users were able to successfully login and sign up but in case they wanted to change their login password they could not. So, we added a feature to allow users to change their existing password. This blog explains the technical details to implement this feature following MVVM architecture and using highly efficient libraries like Retrofit, RxJava, Raziz Labs DbFlow, Data Binding. Specifications We will implement a page where users can enter their old password and new password along with a confirm password field. Their will be a login button to send the password change request to server. Server then return a response and we will provide feedback regarding the request. We are following MVP architecture so there will be a Model class, Fragment class, Presenter class and Network Layer to make network requests. Let’s start with creating ChangePassword model class. There are three fields to store old password, new password and new confirmed password. Several Lombok annotations like @Data, @AllArgsConstructor, @NoArgsConstructor are used to avoid boilerplate code for getters, setters and constructors. @JsonNaming annotation is used to translate the Java Object names to KebabCase when they are serialized. @Data @AllArgsConstructor @NoArgsConstructor @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) public class ChangePassword { public String oldPassword; public String newPassword; @JsonIgnore public String confirmNewPassword; } The layout file is binded to model using Data Binding. There will be three TextInputEditText fields for user input. An option to toggle password visibility and a login button. The Fragment class binds layout file to the Fragment and handle UI stuff. Presenter is called to make Login request when login button is pressed. public class ChangePasswordFragment extends BaseFragment<ChangePasswordPresenter> implements ChangePasswordView { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = DataBindingUtil.inflate(inflater, R.layout.change_password_fragment, container, false); validator = new Validator(binding); AppCompatActivity activity = ((AppCompatActivity) getActivity()); activity.setSupportActionBar(binding.toolbar); ActionBar actionBar = activity.getSupportActionBar(); if (actionBar != null) { actionBar.setHomeButtonEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true); } return binding.getRoot(); } @Override public void onStart() { super.onStart(); getPresenter().attach(this); binding.setOrganizerPassword(getPresenter().getChangePasswordObject()); getPresenter().start(); binding.btnChangePassword.setOnClickListener(view -> { if (!validator.validate()) return; String url = binding.url.baseUrl.getText().toString().trim(); getPresenter().setBaseUrl(url, binding.url.overrideUrl.isChecked()); getPresenter().changePasswordRequest(binding.oldPassword.getText().toString(), binding.newPassword.getText().toString(), binding.confirmNewPassword.getText().toString()); }); } When the Login button is pressed, changePasswordRequest() method is called which makes an asynchronous call to ChangePasswordModel in order to perform the task of sending and receiving data from network in a different thread than the UI thread. Along with making requests, this method also verifies the password typed in confirm password field and send the the error as well as success message to the fragment. public class ChangePasswordPresenter extends AbstractBasePresenter<ChangePasswordView> { public void changePasswordRequest(String oldPassword, String newPassword, String confirmPassword) { if (!newPassword.equals(confirmPassword)) { getView().showError("Passwords Do Not Match"); return; } organizerPasswordObject.setOldPassword(oldPassword); organizerPasswordObject.setNewPassword(newPassword); organizerPasswordObject.setConfirmNewPassword(confirmPassword); changePasswordModel.changePassword(organizerPasswordObject) .compose(disposeCompletable(getDisposable())) .compose(progressiveErroneousCompletable(getView())) .subscribe(() -> getView().onSuccess("Password Changed Successfully"), Logger::logError); } } We are using Retrofit to make POST Request to server using the REST API. @Body annotation denotes the object request body which here contains a Map<String, ChangePassword> object. The Response from server is captured in Observable<ChangePasswordResponse> which is an RxJava Observable. @POST("auth/change-password") Observable<ChangePasswordResponse> changePassword(@Body Map<String, ChangePassword> changePassword); This is the declaration for the…

Continue ReadingChange Password Feature for Open Event Android Organizer App

Create Session in Open Event Android Organizer Application

Open Event Android Organizer Application offered variety of features to Organizers from all over the world to help them host their Events globally but it didn’t had the functionality to create Sessions in the app itself and associate it to Tracks. This feature addition was crucial since it enables Organizer to create Sessions which every common person enquires about before attending and event. In this Blog Post we will see how we added this functionality in the app. Open Event Android Organizer Application is a client for Open Event Server which provides the REST API. Problem There can be various Sessions associated with Tracks for an Event. Open Event API had the endpoint to implement Creating Session but the Orga app didn’t, so we worked on creating a Session in the app. The UI for creating a Session is shown above. User can fill in the necessary details and click on the green Floating Action Button to create a Session. How to implement functionality? We will follow MVP Architecture and use Retrofit 2.x, RxJava, Dagger, Jackson, Data Binding and other industry standard libraries to implement this functionality. Firstly, let’s create Session model class specifying the attributes and relationships to set up in database using RazizLabs DbFlow library. The POJO will be serialized into JSON by Jackson library to be passed on as a part of RequestBody to server. Now we will create SessionApi class that will contain the request details to be passed to Retrofit. @POST denotes a POST request and @Body denotes the requestBody of the request which is a Session object. public interface SessionApi { @POST("sessions") Observable<Session> postSession(@Body Session session); } This is the CreateSessionFragment class that contains the code binding model to the view. The Fragment class implements the CreateSessionView class overriding the method declarations present there. The @Inject annotation of Dagger is used to load singleton presenter instance lazily to improve app’s performance. Event-Id and Track-Id’s are retrieved from Bundle from Fragment Transaction. These are then passed on to presenter when Create Session button is pressed. There are other methods to show binding progressbar, snackbar and other UI components to show progress of the background request to server and database. public class CreateSessionFragment extends BaseFragment<CreateSessionPresenter> implements CreateSessionView { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = DataBindingUtil.inflate(inflater, R.layout.session_create_layout, container, false); validator = new Validator(binding.form); binding.sessionCreate.setOnClickListener(view -> { if (validator.validate()) { getPresenter().createSession(trackId, eventId); } }); return binding.getRoot(); } @Override public void onStart() { super.onStart(); getPresenter().attach(this); binding.setSession(getPresenter().getSession()); } } In the Presenter createSession method is called when create button is pressed in UI. The method attaches track-id and event-id to Session object. This is necessary for Relationship constraints on Session Model. Then after binding all the data to Session object, we pass it on to SessionRepository. The success response is provided to user by passing success response in getView().onSuccess() method. public class CreateSessionPresenter extends AbstractBasePresenter<CreateSessionView> { public Session getSession() { return session; } public void createSession(long trackId, long eventId) { Track track…

Continue ReadingCreate Session in Open Event Android Organizer Application

Create Call for Speakers with Open Event Organizer Android App

In The Open Event Organizer Android app, we were providing variety of features to the user but the functionality to create call for speakers for an Event was missing. This feature was required to attract speakers from all over the world to participate in the event and make it a success. Theis blog explains how we added this feature to the project us following MVVM Architecture and using libraries like Retrofit, RxJava, Db Flow etc. Objective The goal will be to provide an option to the organizer to create Call for Speakers for an Event (if it doesn’t exist already). We will provide the organizer a Floating Action Button in Speakers Call detail layout which will open a fragment which contain all the relevant fields provided by Open Event Server. The organizer can fill up all the fields as per requirement and create the call for speakers. Specifications Let’s move on to the implementation details. First, we will create the model which will contain all the fields offered by server. We are using Lombok library to reduce boilerplate code using annotations. For example, @Data annotation is used to generate getters and setters. @NoArgsConstructor to generate no arguments constructor as the name suggests. Along with that we are using @JonNaming annotation provided by Jackson library to serialize the model using KebabCaseStrategy. Also, the Table annotation provided by Raziz DbFlow library to make a table in database for this model. @Data @Type("speakers-call") @NoArgsConstructor @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) @Table(database = OrgaDatabase.class, allFields = true) public class SpeakersCall {@Id(LongIdHandler.class) @PrimaryKey public Long id;@Relationship("event") @ForeignKey(onDelete = ForeignKeyAction.CASCADE) public Event event; public String announcement; public String hash; public String privacy; public String startsAt; public String endsAt; } To handle this problem we will be using Retrofit 2.3.0 to make Network Requests, which is a REST client for Android and Java by Square Inc. It makes it relatively easy to retrieve and upload JSON (or other structured data) via a REST based Web Service. Also we will be using other awesome libraries like RxJava 2.1.10 (by ReactiveX) to handle tasks asynchronously, Jackson, Jasminb-Json-Api in an MVVM architecture. We will make a POST request to the server . So we specify the declaration in SpeakersCallApi.java @POST("speakers-calls") Observable<SpeakersCall> postSpeakersCall(@Body SpeakersCall speakersCall); We will use the method createSpeakersCall(SpeakersCall speakersCall) in SpeakersCallRepositoryImpl.java to interact with the SpeakersCallApi and make the network request for us. The response will be passed on to the SpeakersCallViewModel method which calls it. @Override public Observable<SpeakersCall> createSpeakersCall(SpeakersCall speakersCall) { if (!repository.isConnected()) { return Observable.error(new Throwable(Constants.NO_NETWORK)); }return speakersCallApi .postSpeakersCall(speakersCall) .doOnNext(created -> { created.setEvent(speakersCall.getEvent()); repository .save(SpeakersCall.class, created) .subscribe(); }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } Next, we will see the code for SpeakersCallViewModel which is using MVVM ViewModel to handle the processing logic for SpeakesCallFragment. Here we are passing SpeakersCallRepository instance in constructor. Methods like getProgress, getError and getSuccess pass on a LiveData Object from ViewModel to Fragment and the latter observes the changes on the object and show these in UI. CreateSpeakersCall method is used to call speakersCallRepository to send a POST request to…

Continue ReadingCreate Call for Speakers with Open Event Organizer Android App

Retrofit2 Rxjava2 Error Response Handling in Open Event Organizer App

In the Open Event Organizer Android app the challenge is to provide user, a readable error description for the input requests. The Organizer App was showing a coded message which was understandable only to a programmer making it unfriendly to the common user. The blog describes how we tackled this problem and implemented a mechanism to provide user friendly error messages for user requests (if any). Let’s consider the scenario when an organizer want to create an Event or maybe a Ticket so what he has to do is fill out a form containing some user input fields and click on submit button to send the data to the server which in turn sends a response for the request. If the information is valid the server sends a HTTP 201 Response and user gets feedback that the Event/Ticket is created. But if the information is not valid the user gets feedback that there is HTTP 422 error in your request. There is no readable description of error provided to the user. To handle this problem we will be using Retrofit 2.3.0 to make Network Requests, which is a REST client for Android and Java by Square Inc. It makes it relatively easy to retrieve and upload JSON (or other structured data) via a REST based Web Service. Further, we will be using other awesome libraries like RxJava 2.1.10 (by ReactiveX) to handle tasks asynchronously, Jackson, Jasminb-Json-Api in an MVP architecture. Let’s move on to the code. Retrofit to the rescue Now we will see how we can extract the details about the error response and provide user a better feedback rather than just throwing the error code. Firstly let’s make the Request to our REST API Server using Retrofit2. @POST("faqs") Observable<Faq> postFaq(@Body Faq faq); Here we are making a POST request and expecting a Observable<Faq> Response from the server.The  @Body annotation indicates the Request Body is an Faq object. Please note that Observable used here is ReactiveX Observable so don’t confuse it with java.util Observable. public class Faq { @Id(LongIdHandler.class) public Long id;  @Relationship("event") @ForeignKey(stubbedRelationship = true, onDelete = ForeignKeyAction.CASCADE) public Event event; public String question; public String answer; } Let’s say the API declares both question and answer as mandatory fields  for creation of an Faq. We supply the following input to the app. question = "Do I need to buy a Ticket to get In ?"; answer = null We used RxJava to make an asynchronous request to server. In case of error we will get a Retrofit throwable and we will pass that on to ErrorUtils.java which will do all the processing and return a readable error message. faqRepository .createFaq(faq) .compose(dispose(getDisposable())) .compose(progressive(getView()))  .doOnError(throwable -> getView().showError(ErrorUtils.getMessage(throwable))) .subscribe(createdFaq -> { getView().onSuccess("Faq Created"); getView().dismiss(); }, Logger::logError); Now we will extract the ResponseBody from the Retrofit throwable. ResponseBody responseBody = ((HttpException) throwable) .response().errorBody(); return getErrorDetails(responseBody); The ResponseBody is a JSON containing all the information about the error. { "errors": [ { "status": "422", "source": { "pointer": "/data/attributes/answer" }, "detail": "Missing data…

Continue ReadingRetrofit2 Rxjava2 Error Response Handling in Open Event Organizer App

Posting Tweet from Loklak Wok Android

Loklak Wok Android is a peer harvester that posts collected tweets to the Loklak Server. Not only it is a peer harvester, but also provides users to post their tweets from the app. Images and location of the user can also be attached in the tweet. This blog explains Adding Dependencies to the project In app/build.gradle: apply plugin: 'com.android.application' apply plugin: 'me.tatarka.retrolambda' android { ... packagingOptions { exclude 'META-INF/rxjava.properties' } } dependencies { ... compile 'com.google.code.gson:gson:2.8.1' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile 'io.reactivex.rxjava2:rxjava:2.0.5' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' }   In build.gradle project level: dependencies { classpath 'com.android.tools.build:gradle:2.3.3' classpath 'me.tatarka:gradle-retrolambda:3.2.0' }   Implementation User first authorize the application, so that they are able to post tweet from the app. For posting tweet statuses/update API endpoint of twitter is used and for attaching images with tweet media/upload API endpoint is used. As, photos and location can be attached in a tweet, for Android Marshmallow and above we need to ask runtime permissions for camera, gallery and location. The related permissions are mentioned in Manifest file first <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> // for location <uses-feature android:name="android.hardware.location.gps"/> <uses-feature android:name="android.hardware.location.network"/>   If, the device is using an OS below Android Marshmallow, there will be no runtime permissions, the user will be asked permissions at the time of installing the app. Now, runtime permissions are asked, if the user had already granted the permission the related activity (camera, gallery or location) is started. For camera permissions, onClickCameraButton is called @OnClick(R.id.camera) public void onClickCameraButton() { int permission = ContextCompat.checkSelfPermission( getActivity(), Manifest.permission.CAMERA); if (isAndroidMarshmallowAndAbove && permission != PackageManager.PERMISSION_GRANTED) { String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }; requestPermissions(permissions, CAMERA_PERMISSION); } else { startCameraActivity(); } }   To start the camera activity if the permission is already granted, startCameraActivity method is called private void startCameraActivity() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File dir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES); mCapturedPhotoFile = new File(dir, createFileName()); Uri capturedPhotoUri = getImageFileUri(mCapturedPhotoFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, capturedPhotoUri); startActivityForResult(intent, REQUEST_CAPTURE_PHOTO); }   If the user decides to save the photo clicked from camera activity, the photo should be saved by creating a file and its uri is required to display the saved photo. The filename is created using createFileName method private String createFileName() { String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmmss").format(new Date()); return "JPEG_" + timeStamp + ".jpg"; }   and uri is obtained using getImageFileUri private Uri getImageFileUri(File file) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { return Uri.fromFile(file); } else { return FileProvider.getUriForFile(getActivity(), "org.loklak.android.provider", file); } }   Similarly, for the gallery, onClickGalleryButton method is implemented to ask runtime permissions and launch gallery activity if the permission is already granted. @OnClick(R.id.gallery) public void onClickGalleryButton() { int permission = ContextCompat.checkSelfPermission( getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE); if (isAndroidMarshmallowAndAbove && permission != PackageManager.PERMISSION_GRANTED) { String[] permissions = { Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }; requestPermissions(permissions, GALLERY_PERMISSION); } else { startGalleryActivity(); } }   For starting the gallery activity, startGalleryActivity is used private void startGalleryActivity() { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); startActivityForResult( Intent.createChooser(intent, "Select images"), REQUEST_GALLERY_MEDIA_SELECTION); }   And finally for location onClickAddLocationButton…

Continue ReadingPosting Tweet from Loklak Wok Android

Data Access Layer in Open Event Organizer Android App

Open Event Organizer is an Android App for Organizers and Entry Managers. Its core feature is scanning a QR Code to validate Attendee Check In. Other features of the App are to display an overview of sales and tickets management. The App maintains a local database and syncs it with the Open Event API Server. The Data Access Layer in the App is designed such that the data is fetched from the server or taken from the local database according to the user's need. For example, simply showing the event sales overview to the user will fetch the data from the locally saved database. But when the user wants to see the latest data then the App need to fetch the data from the server to show it to the user and also update the locally saved data for future reference. I will be talking about the data access layer in the Open Event Organizer App in this blog. The App uses RxJava to perform all the background tasks. So all the data access methods in the app return the Observables which is then subscribed in the presenter to get the data items. So according to the data request, the App has to create the Observable which will either load the data from the locally saved database or fetch the data from the API server. For this, the App has AbstractObservableBuilder class. This class gets to decide which Observable to return on a data request. Relevant Code: final class AbstractObservableBuilder<T> { ... ... @NonNull private Callable<Observable<T>> getReloadCallable() { return () -> { if (reload) return Observable.empty(); else return diskObservable .doOnNext(item -> Timber.d("Loaded %s From Disk on Thread %s", item.getClass(), Thread.currentThread().getName())); }; } @NonNull private Observable<T> getConnectionObservable() { if (utilModel.isConnected()) return networkObservable .doOnNext(item -> Timber.d("Loaded %s From Network on Thread %s", item.getClass(), Thread.currentThread().getName())); else return Observable.error(new Throwable(Constants.NO_NETWORK)); } @NonNull private <V> ObservableTransformer<V, V> applySchedulers() { return observable -> observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } @NonNull public Observable<T> build() { if (diskObservable == null || networkObservable == null) throw new IllegalStateException("Network or Disk observable not provided"); return Observable .defer(getReloadCallable()) .switchIfEmpty(getConnectionObservable()) .compose(applySchedulers()); } }   The class is used to build the Abstract Observable which contains both types of Observables, making data request to the API server and the locally saved database. Take a look at the method build. Method getReloadCallable provides an observable which will be the default one to be subscribed which is a disk observable which means data is fetched from the locally saved database. The method checks parameter reload which if true suggests to make the data request to the API server or else to the locally saved database. If the reload is false which means data can be fetched from the locally saved database, getReloadCallable returns the disk observable and the data will be fetched from the locally saved database. If the reload is true which means data request must be made to the API server, then the method returns an empty observable. The method getConnectionObservable returns a network observable…

Continue ReadingData Access Layer in Open Event Organizer Android App
Read more about the article Automatic handling of view/data interactions in Open Event Orga App
Abstract 3d white geometric background. White seamless texture with shadow. Simple clean white background texture. 3D Vector interior wall panel pattern.

Automatic handling of view/data interactions in Open Event Orga App

During the development of Open Event Orga Application (Github Repo), we have strived to minimize duplicate code wherever possible and make the wrappers and containers around data and views intelligent and generic. When it comes to loading the data into views, there are several common interactions and behaviours that need to be replicated in each controller (or presenter in case of MVP architecture as used in our project). These interactions involve common ceremony around data loading and setting patterns and should be considered as boilerplate code. Let’s look at some of the common interactions on views: Loading Data While loading data, there are 3 scenarios to be considered: Data loading succeeded - Pass the data to view Data loading failed - Show appropriate error message Show progress bar on starting of the data loading and hide when completed If instead of loading a single object, we load a list of them, then the view may be emptiable, meaning you’ll have to show the empty view if there are no items. Additionally, there may be a success message too, and if we are refreshing the data, there will be a refresh complete message as well. These use cases present in each of the presenter cause a lot of duplication and can be easily handled by using Transformers from RxJava to compose common scenarios on views. Let’s see how we achieved it. Generify the Views The first step in reducing repetition in code is to use Generic classes. And as the views used in Presenters can be any class such as Activity or Fragment, we need to create some interfaces which will be implemented by these classes so that the functionality can be implementation agnostic. We broke these scenarios into common uses and created disjoint interfaces such that there is little to no dependency between each one of these contracts. This ensures that they can be extended to more contracts in future and can be used in any View without the need to break them down further. When designing contracts, we should always try to achieve fundamental blocks of building an API rather than making a big complete contract to be filled by classes. The latter pattern makes it hard for this contract to be generally used in all classes as people will refrain from implementing all its methods for a small functionality and just write their own function for it. If there is a need for a class to make use of a huge contract, we can still break it into components and require their composition using Java Generics, which we have done in our Transformers. First, let’s see our contracts. Remember that the names of these Contracts are opinionated and up to the developer. There is no rule in naming interfaces, although adjectives are preferred as they clearly denote that it is an interface describing a particular behavior and not a concrete class: Emptiable A view which contains a list of items and thus can be empty public interface Emptiable<T>…

Continue ReadingAutomatic handling of view/data interactions in Open Event Orga App