Hiding Payment Options for Free Tickets in Open Event Android
Payment Options spinner allows the user to pick any convenient payment method for an order however, Payment Options should not be shown if the total worth of order is zero or the event is free. This blog post will guide you on how its implemented in Open Event Android. Following are the sequence of steps that are followed for this
Allow the user to select tickets and their quantity
Use DAO method to get the prices of the tickets selected by the user
Iterate through the List and keep storing the prices
Display Payment Selector for order with the total amount greater than zero
Given below is DAO method to get the list of prices for different tickets. The method makes a simple select statement and returns price attribute of tickets with id in passed ids. A single list of float is returned by the function.
@Query(“SELECT price from Ticket WHERE id in (:ids)”) fun getTicketPriceWithIds(ids : List<Int>): Single<List<Float>>
This DAO method is then exposed to other parts (ViewModels, Fragments) using service layer class method getTicketPriceWithIds which just makes calls to DAO method and return the result provided by it. Service classes are used for separation of concerns.
fun getTicketPriceWithIds(ids: List<Int>): Single<List<Float>> { return ticketsDao.getTicketPriceWithIds(ids) }
When a list of tickets and their quantity is specified by the user the prices of the ticket are fetched using the above-explained methods. These are then stored it in a List of pair with the first element being ticket id and second its quantity.
fun updatePaymentSelectorVisibility(ticketIdAndQty: List<Pair<Int, Int>>?) { val ticketIds = ArrayList<Int>() ticketIdAndQty?.forEach { if (it.second > 0) ticketIds.add(it.first) } compositeDisposable.add(ticketService.getTicketPriceWithIds(ticketIds) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ var total = 0.toFloat() it?.forEach { if (it.toFloat() > 0) total += it.toFloat() } paymentSelectorVisibility.value = total != 0.toFloat() }, { Timber.e(it, “Error Loading tickets!”) })) }
The above method controls the visibility boolean for payment selector, it essentially iterates over the List<Pair<Int, Int>> and add all the ticket id to a temporary list if quantity for that ticket is greater than zero after this prices for the tickets in temporary list is fetched and sum total is calculated. If this total is greater than zero the visibility boolean is set else reset.
Finally an observable is set on this boolean which automatically updates the UI whenever boolean changes accordingly.
The Open Event Organiser Android App is currently released in the Alpha phase on the Google Play Store here. This blog post explains how the speaker details feature has been implemented in the app.
Model
The model for Speaker is pretty straightforward. It includes the personal details of the speaker such as name, biography, country, social media profiles, designation etc. Apart from these details, every instance of speaker is associated with a single event. A speaker will also have multiple instances of sessions. Full implementation of the speaker’s model can be found here.
Network Call
We use Retrofit in order to make the network call and Jackson Factory to deserialize the data received from the call into an instance of the speaker model. The following endpoint provides us with the required information:
In any typical android application using both network calls and data persistence, there is a need of a repository class to handle them. Speaker Repository handles the network call to the API in order to fetch the speaker details. It then saves the data returned by the api into the database asynchronously. It also ensures that we send the latest data that we have stored in the database to the view model. Given below is the full implementation for reference:
The View Model is responsible for fetching the necessary details from the repository and displaying it in the view. It handles all the view binding logic. The most important method in the SpeakerDetailsViewModel is the getSpeakers method. It accepts a speaker id from the fragment, queries the repository for the details of the speaker and returns it back to the fragment in the form of a LiveData. Below is the full implementation of the getSpeakers method:
We add the disposable to a composite disposable and dispose it in the onCleared method of the View Model. The full implementation of the View Model can be found here.
Fragment
The SpeakerDetailsFragment acts as the view and is responsible for everything the user sees on the screen. It accepts the id of the speaker whose details are to be displayed in the constructor. When an instance of the fragment is created it sets up it’s view model and inflates it’s layout using the Data binding framework.
In the onStart method of the fragment we load the data by calling the getSpeaker method in the view model. Then we set up the RecyclerView for the sessions associated with the speaker. Lastly we also set up the refresh listener which can be used by the user to refresh the data.
Once the data is returned we simply set it on the layout by calling setSpeaker on the binding.
@Override
public void showResult(Speaker item) {
binding.setSpeaker(item);
}
The full implementation of the SpeakerDetailsFragment can be found here.
Sessions Adapter
The SessionsAdapter is responsible for handling the RecyclerView of sessions associated with the speaker. The most important method in the adapter is the setSessions method. It accepts a list of sessions and shows it as the contents of the recycler view. It uses the DiffUtil.calculateDiff method to create a DiffResult which will be used by the adapter to figure out where to insert items.
protected void setSessions(final List<Session> newSessions) {
if (sessions == null) {
sessions = newSessions;
notifyItemRangeInserted(0, newSessions.size());
} else {
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return sessions.size();
}
@Override
public int getNewListSize() {
return newSessions.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return sessions.get(oldItemPosition).getId()
.equals(newSessions.get(newItemPosition).getId());
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return sessions.get(oldItemPosition).equals(newSessions.get(newItemPosition));
}
});
sessions = newSessions;
result.dispatchUpdatesTo(this);
}
}
The full implementation of the Adapter can be found here.
Spinners are basically drop down menu which provides an easy way to select an item from a set of items. Spinner is used in Open Event Android to allow user select quantity of tickets as shown in the image above. This blog post will guide you on how its implemented in Open Event Android.
The above code spinnet will create a spinner type view with 30dp height and width, spinnerMode can be dialog or dropDown following are example spinner for both of the modes left being dialog type and right dropDown type.
(Image Source: Stack Overflow)
Set Up Adapter and Populate Data on Spinner
To show the list of acceptable Quantities for a Ticket, create an ArrayList of String and add all values from ticket.minOrder to ticket.maxOrder along with a zero option. Since ArrayList is of String and the values are integer they need to be converted to String using Integer.toString(i) method. Following code snippet will give you more understanding about it.
val spinnerList = ArrayList<String>() spinnerList.add(“0”) for (i in ticket.minOrder..ticket.maxOrder) { spinnerList.add(Integer.toString(i)) }
We will also need to set up an onItemSelectedListener to listen for Item Selections and override onItemSelected and onNothingSelected functions in it. Whenever an item is selected onItemSelected is called with arguments such as view, position of the selected item, id etc, these can be used to find the selected Quantity from the ArrayList of acceptable Quantities.
Since Spinner is an Adapter Based view we need to create a SpinnerAdapter and attach it with the Spinner. If you want to create custom Spinners you would have to create a custom adapter for it along with a layout for its row elements or else one can use library-provided layout. Given below is the implementation for library-provided spinner row type. There are different options available for row type we are using R.layout.select_dialog_singlechoice because we need single selectable Quantity spinner along with Radio button for every ticket.
In Open Event Android whenever App is started events are fetched for the location given by the user, since we have locally added isFavorite extra field for every event it is necessary to be updated for all the events returned in the API response before inserting it into our database otherwise all the favorite related information would be lost. This blog post will guide you on how its done in Open Event Android.
The sequence of steps followed
Take the IDs of events being saved into the database
Use DAO method which does “SELECT id from Event where favorite = 1 AND id in :eventIds and pass API returned eventIds to this function
Set the old favorited on new event objects
Save them in database
Let’s see how all of these steps are performed in greater details. Whenever user gives in a location following function is called
Here we are extracting all the Ids of events returned in the API response and then calling getFavoriteEventWithinIds on it. The latter takes the list of eventIds and return the Ids of events which are favorite out of them. This is then passed to the function updateFavorite along with the API returned Events. Following is the implementation of updateFavorite method.
fun updateFavorites(apiEvents: List<Event>, favEventIds: List<Long>): Single<List<Event>> { apiEvents.map { if (favEventIds.contains(it.id)) it.favorite = true } eventDao.insertEvents(apiEvents) val eventIds = apiEvents.map { it.id }.toList() return eventDao.getEventWithIds(eventIds) }
updateFavorite checks for all events in the list of events whether if its Id is present in the favorite event ids it sets favorite field of that event true. After the favorites for the list of events are updated they are inserted into the database using DAO method insertEvents. The last task is to take the fetch these events again from the database, to do this first we extract the Ids of the events we just inserted into the database and call the DAO method getEventsWithIds passing the extracted eventIds, getEventsWithids simply returns the Events with given Ids.
Given below are the implementations of the functions getEventWithIds and getFavoriteEventWithinIds
@Query(“SELECT * from Event WHERE id in (:ids)”) fun getEventWithIds(ids: List<Long>): Single<List<Event>> @Query(“SELECT id from Event WHERE favorite = 1 AND id in (:ids)”) fun getFavoriteEventWithinIds(ids : List<Long>): Single<List<Long>>
getEventWithIds simply makes a select query and checks for events whose ids lies in the ids passed to the method
getFavoriteEventWithinids returns the Ids of the favorite event out of the list of event id passed to the method.
The favorite events feature in Open Event Android allows any user to favorite an event and those events can are available in the favorite events fragment which is easily accessible from the bottom navigation bar. This blog article will walk you through on how favorite works in Open Event Android. There are a couple of different ways to do it, in Open Event Android we are keeping track of favorite events using a favorite Boolean stored along with the information of the event ie we have added a favorite boolean field inside the event entity class. However, this method has its own pros and cons. The big plus point of this method is the fact that we can simply check the favorite field of the event object to check if that particular event is favorite or not. But since we have set onConflict strategy as replace in the event insert DAO method (shown below)
@Insert(onConflict = REPLACE) fun insertEvents(events: List<Event>)
This leads to a big problem when the same event is fetched or is present in the response while inserting the favorite field will be set to default value (false) and the favorite information would be lost. Hence extra care needs to be taken while updating events.
Given following is the event model class for serializing / deserializing JSON responses. Note the extra field favorite added here which helps us to provide user favorite feature locally.
Since we added a new field to the Event model class, to access the favorite events we will have to create DAO methods. To fetch all the favorite events we will have to create a Query method which returns all the events wherever favorite property is set. This can be done using a simple select statement which looks for events with favorite boolean set ie. 1, the method implementation is shown below.
@Query(“SELECT * from Event WHERE favorite = 1”) fun getFavoriteEvents(): Flowable<List<Event>>
To set an event favorite we will have to add one more method to the EventDao. We can create a generalized method which sets according to the boolean passed as a parameter. The function implementation is shown below, setFavorite takes in EventId, Id of the event which has to be updated and the boolean favorite which is what the new value for the favorite field is. setFavorite makes an SQL update query and matches for EventId and sets favorite for that event.
@Query(“UPDATE Event SET favorite = :favorite WHERE id = :eventId”) fun setFavorite(eventId: Long, favorite: Boolean)
In Open Event Android we use the service class which can be used to expose the DAO methods to the rest of the project. The idea behind creating service layer is the separation of concerns this service method is then called from the view model function (following the MVVM approach). Given following is the implementation of the get and set favorite methods.
The method getFavoriteEvents calls the DAO method to fetch the favorite evens and returns a flowable list of favorite events and setFavorite method generates a completable from the DAO action to favorite any event.
EventViewModel uses these methods and specifies where the observables should be observedOn and subscribedOn also defines what should happen when some error occurs. Given below is implementation for the two methods.
For every event, we have a floating action bar to toggle the favorite state of the same whenever user clicks on the FAB it checks the current state of the event and calls the view model function setFavorite passing the Event Id of the clicked event and the negation of current event.favorite state.
val favouriteFabClickListener = object : FavoriteFabListener { override fun onClick(eventId: Long, isFavourite: Boolean) { eventsViewModel.setFavorite(eventId, !isFavourite) } }
What we discussed in this blog post works well unless we are not inserting the events with the same event id, this will cause replace on the stored events and current favorite information will be lost. To overcome this limitation we follow the following procedure
Fetch event id’s of all the events returned by the API response
Use the DAO method to find out the id’s of favorite events out of these
Set the favorite in the new events where IDs are in the ids returned in the second point
Insert the events into the database
Return the events from the database with ID’s as of point one
Feedback from users is one of the most important aspect of software development. This helps developers collect data about the bugs that need to be fixed and the improvements that need to be done in a software. This also provides them with an insight into what needs to be done for the long term sustainability of the app.
SUSI.AIAndroid, web and iOS clients allow user to rate the SUSI Skills allow the user to post feedback about a skill. Users can send their invaluable feedback about how helpful a skill is for them and how could it be improved. This blog focuses on the implementation of the feature to post feedback from the SUSI.AI Android client.
Steps to post feedback
Log into the app. You will see the Chat Activity once you are logged into the app.
Tap the FAB on the upper right corner of the Chat Activity.
You will see the list of all skills.
Tap on any skill of your choice.
Write feedback about SUSI skill
Click on POST button to post the skill feedback. A toast, with the message “Skill feedback updated”, will appear at the bottom of the screen.
Note : An anonymous user cannot submit feedback for the skill. You must be logged-in in order to post feedback. If you are not logged-in, the post feedback section would not be visible. Instead you will see a text message saying “Please login to post feedback”.
Implementation of post skill feedback feature
Case 1 : The user is logged in i.e. the access token is not null.
This is how the post feedback UI looks like, when the user is logged in :
Now, add the following code inside a service to make a post request to feedbackSkill.json API.
/** * Post feedback given by the user for the skill to the server * * @param model Model of the skill (e.g. general) * @param group Group of skill (e.g. Knowledge) * @param language Language directory in which the skill resides (e.g. en) * @param skill Skill Tag of object in which the skill data resides * @param feedback Feedback to be sent to the server * @return the call */
@POST("/cms/feedbackSkill.json")
Call<PostSkillFeedbackResponse> postFeedback(
@Query("model") String model,
@Query("group") String group,
@Query("language") String language,
@Query("skill") String skill,
@Query("feedback") String feedback);
Further, attach an OnClickListener with the POST button and call the postFeedback() present in the presenter layer, as can be seen in the following code :
/*** Set up the feedback section** If the user is logged in, show the post feedback section otherwise display an appropriate message*/privatefunsetFeedback() {
if (PrefManager.getToken() != null) {
tvPostFeedbackDesc.visibility = View.VISIBLE
layoutPostFeedback.visibility = View.VISIBLE
buttonPost.setOnClickListener {
if (etFeedback.text.toString().isNotEmpty()) {
skillDetailsPresenter.postFeedback(skillData.model, skillData.group, skillData.language,
skillTag, etFeedback.text.toString())
} else {
Toast.makeText(context, getString(R.string.toast_empty_feedback), Toast.LENGTH_SHORT).show()
}
}
} else {
tvAnonymousPostFeedback.visibility = View.VISIBLE
}
}
The postFeedback() in the presenter will now invoke the postFeedback() method present in the model as follows :
Once the call is made successfully to the server, the next step is to update the UI. This is achieved with the help of the onUpdateFeedbackModelSuccess() called above, if the response is received.
Clearly, if the response is successful and the response body is non-null, it implies that the feedback submitted by the user has been successfully updated on the server. Thus, we display a toast to notify the user of the successful submission of the feedback. This is achieved with the help of the updateFeedback() method in the view, as follows :
Graphs and charts provide a visual representation of the data. They provide a clearer and quicker understanding of the impact of certain statistics. Thus, SUSI.AI Android app makes use of bar charts to display statistics related to user ratings for SUSI skills.This blog guides through the steps to create a Horizontal Bar Chart, using MPAndroidChart library, that has been used in the SUSI.AI Android app skill details page to display the five star skill rating by the users.
On vertical axis : Labels of the rating shown On horizontal axis : Percentage of total number of users who rated the skill with the corresponding number of stars on the vertical axis
Step – 1 : Add the required dependencies to your build.gradle.
<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><!-- Add a Horizontal Bar Chart using MPAndroidChart library --><com.github.mikephil.charting.charts.HorizontalBarChartandroid:id="@+id/skill_rating_chart"android:layout_width="match_parent"android:layout_height="match_parent"/></android.support.constraint.ConstraintLayout>
Step – 3 : Create an Activity and initialize the Horizontal Bar Chart.
Step – 4 : Create a method in your MainActivity to set up the basic properties and the axes.
/*** Set up the axes along with other necessary details for the horizontal bar chart.*/funsetSkillGraph(){
skillRatingChart = skill_rating_chart //skill_rating_chart is the id of the XML layout
skillRatingChart.setDrawBarShadow(false)
val description = Description()
description.text = ""
skillRatingChart.description = description
skillRatingChart.legend.setEnabled(false)
skillRatingChart.setPinchZoom(false)
skillRatingChart.setDrawValueAboveBar(false)
//Display the axis on the left (contains the labels 1*, 2* and so on)val xAxis = skillRatingChart.getXAxis()
xAxis.setDrawGridLines(false)
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM)
xAxis.setEnabled(true)
xAxis.setDrawAxisLine(false)
val yLeft = skillRatingChart.axisLeft
//Set the minimum and maximum bar lengths as per the values that they represent
yLeft.axisMaximum = 100f
yLeft.axisMinimum = 0f
yLeft.isEnabled = false//Set label count to 5 as we are displaying 5 star rating
xAxis.setLabelCount(5)
//Now add the labels to be added on the vertical axisval values = arrayOf("1 *", "2 *", "3 *", "4 *", "5 *")
xAxis.valueFormatter = XAxisValueFormatter(values)
val yRight = skillRatingChart.axisRight
yRight.setDrawAxisLine(true)
yRight.setDrawGridLines(false)
yRight.isEnabled = false//Set bar entries and add necessary formatting
setGraphData()
//Add animation to the graph
skillRatingChart.animateY(2000)
}
Here is the XAxisValueFormatter class that is used to add the custom labels to the vertical axis :
publicclassXAxisValueFormatterimplements IAxisValueFormatter {private String[] values;publicXAxisValueFormatter(String[] values){this.values= values;}@Overridepublic String getFormattedValue(float value, AxisBase axis){// "value" represents the position of the label on the axis (x or y)returnthis.values[(int) value];}}
Step – 5 : Set the bar entries.
/*** Set the bar entries i.e. the percentage of users who rated the skill with* a certain number of stars.** Set the colors for different bars and the bar width of the bars.*/privatefunsetGraphData() {
//Add a list of bar entriesval entries = ArrayList<BarEntry>()
entries.add(BarEntry(0f, 27f))
entries.add(BarEntry(1f, 45f))
entries.add(BarEntry(2f, 65f))
entries.add(BarEntry(3f, 77f))
entries.add(BarEntry(4f, 93f))
//Note : These entries can be replaced by real-time data, say, from an API
......
}
Step – 6 : Now create a BarDataSet.
To display the data in a bar chart, you need to initialize a BarDataSet instance. BarDataSet is the Subclass of DataSet class. Now, initialize the BarDataSet and pass the argument as an ArrayList of BarEntry object.
val barDataSet = BarDataSet(entries, "Bar Data Set")
Step – 7 : Assign different colors to the bars (as required).
privatefunsetGraphData() {
.....
//Set the colors for bars with first color for 1*, second for 2* and so on
barDataSet.setColors(
ContextCompat.getColor(skillRatingChart.context, R.color.md_red_500),
ContextCompat.getColor(skillRatingChart.context, R.color.md_deep_orange_400),
ContextCompat.getColor(skillRatingChart.context, R.color.md_yellow_A700),
ContextCompat.getColor(skillRatingChart.context, R.color.md_green_700),
ContextCompat.getColor(skillRatingChart.context, R.color.md_indigo_700)
.....
)
Step – 8 : Populate data into Bar Chart.
To load the data into Bar Chart, you need to initialize a BarData object with bardataset. This BarData object is then passed into setData() method to load Bar Chart with data.
//Set bar shadows
skillRatingChart.setDrawBarShadow(true)
barDataSet.barShadowColor = Color.argb(40, 150, 150, 150)
val data = BarData(barDataSet)
//Set the bar width//Note : To increase the spacing between the bars set the value of barWidth to < 1f
data.barWidth = 0.9f//Finally set the data and refresh the graph
skillRatingChart.data = data
skillRatingChart.invalidate()
}
Your Horizontal Bar Chart is now ready. Note: You can format the labels as per your need and requirement with the help of XAxisValueFormatter.
As per the official GitHub repository of Timber : “Timber is a logger with a small, extensible API which provides utility on top of Android’s normal Log class”. It is a flexible logging library for Android which makes logging a lot more convenient. In this blog you will learn how to use Timber in SUSI.AI Android app.
To begin, add Timber to your build.gradle and refresh your gradle dependencies.
Install any Tree instances that you want in the onCreate() of your application class.
Call Timber’s static methods everywhere throughout the app.
You can add the following code to your application class :
@Override
public void onCreate() {
super.onCreate();
…..
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree() {
//Add the line number to the tag
@Override
protected String createStackElementTag(StackTraceElement element) {
return super.createStackElementTag(element) + ": " + element.getLineNumber();
}
});
} else {
//Release mode
Timber.plant(new ReleaseLogTree());
}
}
private static class ReleaseLogTree extends Timber.Tree {
@Override
protected void log(int priority, String tag, @NonNull String message,
Throwable throwable) {
if (priority == Log.DEBUG || priority == Log.VERBOSE || priority == Log.INFO) {
return;
}
if (priority == Log.ERROR) {
if (throwable == null) {
Timber.e(message);
} else {
Timber.e(throwable, message);
}
}
}
}
Timber ships with a ‘Debug Tree’ that provides all the basic facilities that you are very used to in the common Android.log framework. Timber has all the logging levels that are used in the normal Android logging framework which are as follows:
Log.e: Use this tag in places like inside a catch statement where you are aware that an error has occurred and therefore you’re logging an error.
Log.w: Use this to log stuff you didn’t expect to happen but isn’t necessarily an error.
Log.i: Use this to post useful information to the log. For instance, a message that you have successfully connected to a server.
Log.d: Use this for debugging purposes. For instance, if you want to print out a bunch of messages so that you can log the exact flow of your program or if you want to keep a log of variable values.
Log.v: Use this if, for some reason, you need to log every little thing in a particular part of your app.
Log.wtf: Use this when you encounter a terrible failure.
You can useTimber.e, Timber.w, Timber.i, Timber.d and Timber.vrespectively for the logging levels mentioned above. However, there is a small difference. In Android logging framework the exception is passed as the last parameter. But, when using Timber, you need to provide the exception as the first parameter. Interestingly, while using Timber you don’t have to provide a TAG in logging calls because it automatically does that for you. It uses the file name, where you are logging from, as the TAG. To log an exception you can simply write:
Timber.e(exception, "Message");
Or a null message if you want:
Timber.e(exception, null);
A simple message can be logged sent via Crashlytics too:
Timber.e("An error message");
Did you notice? There is no TAG parameter. It is automatically assigned as the caller class’ name. Now, you can use Timber for logging in SUSI Android app.
In PSLab android app, we have included sensor instruments which use either inbuilt sensor of smartphone or external sensor to record sensor data. For example, Lux Meter uses the light sensor to record lux data but the problem is that these instruments doesn’t contain settings option to configure the sensor record-setting like which sensor to use, change the update period etc.
Therefore, we need to create a settings page for the Lux Meter instrument which allows the user to modify the properties of the sensor instrument. For that I will use AndroidPreferenceAPIsto build a settings interface that is similar to setting activity in any other android app.
The main building block of the settings activity is the Preference object. Each preference appears as a single setting item in an activity and it corresponds to key-value pair which stores the settings in default Shared Preferences file. Every time any setting is changed by the user the Android will store the updated value of the setting in the default shared preferences which we can read in any other activity across the app.
In the following steps I will describe instruction on how to create the setting interface in Android app:
For this step, I have created a preference screen which will be inflated when the settings fragment is being created.
I created a file named “lux_meter_settings.xml” and place it in the res/xml/ directory.
The root node for the XML file must be a <PreferenceScreen> element. We have to add eachPreference within this element. Each child I added within the <PreferenceScreen> element appears as a single item in the list of settings.
The preference which I have used are:
<EdittextPreference>This preference opens up a dialog box with edit text and stores whatever value is written by the user in the edit text. I have used this preference for inputting update period and high limit of data during recording.
<CheckboxPreference> shows an item with a checkbox for a setting that is either enabled or disabled. The saved value is a boolean (true if it’s checked). I have used this preference for enabling or disabling location data with the recorded data.
<ListPreference> opens a dialog with a list of radio buttons. The saved value can be any one of the supported value types. I have used this preference to allow the user to choose between multiple sensor types.
<?xml version="1.0" encoding="utf-8"?><PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"><EditTextPreferenceandroid:key="setting_lux_update_period"android:title="@string/update_period"android:dialogTitle="@string/update_period"android:defaultValue="1000"android:dialogMessage="Please provide time interval(in ms) at which data will be updated"android:summary="Update period is 900ms"/><EditTextPreferenceandroid:key="setting_lux_high_limit"android:title="High Limit"android:dialogTitle="High Limit"android:defaultValue="2000"android:dialogMessage="Please provide maximum limit of LUX value to be recorded"android:summary="High Limit is 2000 Lux"/><CheckBoxPreferenceandroid:defaultValue="false"android:key="include_location_sensor_data"android:summary="Include the location data in the logged file"android:title="Include Location Data" /></PreferenceScreen>
The above XML file will produce a layout as shown in Figure 1 below:
Step 3 Implementing the backend of setting interface
As android Documentation clearly states:
As your app’s settings UI is built using Preference objects instead of View objects, you need to use a specialized Activity or Fragment subclass to display the list settings:
If your app supports versions of Android older than 3.0 (API level 10 and lower), you must build the activity as an extension of the PreferenceActivity class.
On Android 3.0 and later, you should instead use a traditional Activity that hosts a PreferenceFragment that displays your app settings.
Therefore, I first created an Activity named “SettingsActivity” which will act as a host for a Fragment. This gist contains code for SettingsActivity which I defined in the PSLab android app which will show the Setting Fragment.
Now, for setting fragment I have created a new fragment and name it “LuxMeterSettingsFragment” and make that fragment class extends the PreferenceFragmentCompat class and for which I needed to add this import statement.
This method is called when the Android is creating the Preferences that is when we need to call the method ‘setPreferencesFromResource()’ and pass the resource file in which we have defined our preferences along with the root key which comes as a parameter. Here, the Android will create preferences referred by the key which we have provided and initialize it to the default value in the default SharedPreferences file.
Step 4 Providing setting option to navigate to setting activity
First I included a setting option in the menu file which is getting inflated in the Lux Meter Activity toolbar as shown in below code.
Then, heading over to Lux Meter Activity in the onOptionItemSelected() method I added below code in which I created intent to open Setting Activity when the setting option is selected.
@OverridepublicbooleanonOptionsItemSelected(MenuItem item){
case R.id.settings:
Intent settingIntent = new Intent(this, SettingsActivity.class);
settingIntent.putExtra("title", "Lux Meter Settings");
startActivity(settingIntent);
break;
}
After this step, we can see the settings option in Lux Meter Activity as shown in Figure 2
To see if its working opens the app -> open Lux Meter Instrument-> Open Overflow Menu -> click on Lux Meter Setting Option to open settings.
Here we can the Lux Meter Setting as shown by the Figure 3.
Step 5 Implementing listener for change in Preferences item
Now, to register this listener with the Preference Screen which we will do it in the onResume() method of the fragment and deregister the listener in the onPause() method to correspond with the lifecycle of the fragment.
@Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
Thus we have successfully implemented settings for the Lux Meter Instrument.
The Open Event Serverenables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues. This blog post explains how Paypal has been integrated in the Open Event Server in order to accept payments for tickets.
The integration of Paypal in the server involved the following steps:
An endpoint to accept the Paypal token from the client applications.
Using the token to get the approved payment details.
Capturing the payment using the fetched payment details.
Endpoint for Paypal token
The server exposes an endpoint to get the Paypal token in order to accept payments.
The above endpoint accepts the Paypal token and uses that to get the payment details from Paypal and then capture the payments.
Getting Approved Payment Details
We use the Paypal Name-Value pair API in the project. First we get the credentials of the event organizer who will be accepting the payments using a call to the get_credentials helper method. It returns the data as the following dictionary:
Next, we use the credentials to get the approved payment details from paypal using the following code snippet.
@staticmethoddefget_approved_payment_details(order, credentials=None):
ifnot credentials:
credentials = PayPalPaymentsManager.get_credentials(order.event)
ifnot credentials:
raiseException('PayPal credentials have not been set correctly')
data = {
'USER': credentials['USER'],
'PWD': credentials['PWD'],
'SIGNATURE': credentials['SIGNATURE'],
'SUBJECT': credentials['EMAIL'],
'METHOD': 'GetExpressCheckoutDetails',
'VERSION': PayPalPaymentsManager.api_version,
'TOKEN': order.paypal_token
}
if current_app.config['TESTING']:
return data
response = requests.post(credentials['SERVER'], data=data)
return json.loads(response.text)
Capturing the payments
After successfully fetching the payment details, the final step is to capture the payment. We set the amount to be charged to the amount of the order and the payer_id to be the payer id received from step 2. Then we simply make a POST request to the Paypal nvp server and capture the payments. The below method is responsible for executing this task:
You must be logged in to post a comment.