Monday, 29 April 2019

Economics Festival app - privacy policy

Privacy Policy
Claudio Agostini built the Economics Festival app as a Free app. This SERVICE is provided by Claudio Agostini at no cost and is intended for use as is.
This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service.
If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Economics Festival unless otherwise defined in this Privacy Policy.
Information Collection and Use
For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to account id, event's favorite sessions. The information that I request will be retained on your device and is not collected by me in any way.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
Log Data
I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics.
Cookies
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
Service Providers
I may employ third-party companies and individuals due to the following reasons:
  • To facilitate our Service;
  • To provide the Service on our behalf;
  • To perform Service-related services; or
  • To assist us in analyzing how our Service is used.
I want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
Security
I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security.
Links to Other Sites
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
Children’s Privacy
These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do necessary actions.
Changes to This Privacy Policy
I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
Contact Us
If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me.
This privacy policy page was created at privacypolicytemplate.net and modified/generated by App Privacy Policy Generator

Wednesday, 7 March 2018

Animations with ConstraintLayout and the transition framework

In this post I'd like to share an interesting tutorial published on AndroidPub by Leonardo Pirro:
Build awesome animations with 7 lines of code using ConstraintLayout

You can find a sample app on GitHub here:
sample app in GitHub

The tutorial is about animating layout changes using the android's transition framework with constraints in a ConstraintLayout.
To begin with, let's see a preview of the animation:

preview of the animation

There are some things I would like to point out about this animation...

Layout files

As you can see from the sample app, there are basically 2 layouts: the starting layout (at the beginning of the animation) and the ending layout (at the end of the animation).
The two layouts are almost identical (they have the same widgets), but with different constraints.
For example in the first layout the title and subtitle have the right constraint set on the left of the ImageView, so they are just outside the screen and therefore not visible. This is their starting position at the beginning of the animation.
The same applies to the TextView containing the description. In the first layout it has its top constraint set to the bottom of the ImageView, so it's just outside the screen and therefore not visible.
In the second layout the same widgets are visible because they use different constraints: that's the final position in the animation.

ConstraintSet

This class allows you to define programmatically a set of constraints to be used with ConstraintLayout. It lets you create and save constraints, and apply them to an existing ConstraintLayout.
In the example, using the method clone() we can assign to the ConstraintSet the constraints of the second layout.

Transition framework

The animation makes use of the Android's transition framework. To be more specific, it uses a transition without scenes using the method "beginDelayedTransition" as described here:
Apply a transition without scenes

The sample uses the ChangeBounds transition (move and resize views).

Animation

The animation works as follows:

  1. we retrieve the constraints from the second layout and store them in a ConstraintSet
  2. we define a Transition (ChangeBounds) with an interpolator and execution time
  3. we invoke the method "beginDelayedTransition" to mark the beginning of the animation
  4. we make the changes that must be animated: these simply consist of applying the constraints of the second layout to the first layout
One thing to notice is that the two layouts are identical except for the constraints, which define the position of the widgets. By animating the constraints we can animate the position of the widgets.
Another thing to notice is that the transition is always applied to the first layout (named "constraint"): the first time we apply the constraints of the second layout, the secont time we apply the constraints of the first layout, and so on...


Friday, 10 November 2017

AsyncTasks done right

As an Android developer you’ve probably already used AsyncTasks to perform long-running tasks in a separate thread and deliver the results back to the main Activity.
AsyncTask are particularly well suited for Activities, because they have coordinated callback methods that run:

  • in a separate thread (doInBackground), to perform long-running operations;
  • in the main thread (onPostExecute, onProgressUpdate, etc.) to display the results in the UI.

To be more precise, AsyncTasks are generally used for tasks that don’t last too much and are related to the content of an Activity (for example, saving the data provided by the user in a local database).

For long-running operations it is always better to use a Service, because the lifecycle of a Service is not so much affected by the lifecycle of the Activity (what happens if the user closes the app while a background thread is ongoing?).
Anyway, you’ve probably used AsyncTasks in a way like this:
public class MyActivity extends AppCompatActivity {
    
    // ...
    new MyAsincTask().execute(...);
    
    private class MyAsincTask extends AsyncTask {
        @Override
        protected Integer doInBackground(Integer... integers) {
            // long running task
        }

        @Override
        protected void onPostExecute(Integer integer) {
            // display results in MyActivity
        }
    }
}

Within Android Studio Lint gives you a warning, saying that non-static inner and anonymous classes could cause memory leaks.
What’s the problem exactly? Well, in Java non-static inner and anonymous classes hold an implicit reference to the outer class. In our example, the AsyncTask holds an implicit reference to the containing Activity. If we launch an AsyncTask to do some work in the background and, while the operation is ongoing, the user closes the app, the AsyncTask keeps an implicit reference to the Activity: this reference can’t be garbage collected, thus creating a memory leak.

To solve this problem we have to convert our AsyncTask in a static class. Static classes in Java are top-level classes and they don’t hold any implicit reference to the containing Activity.
This technique, however, raises another problem. When the AsyncTask has completed the background task and the method onPostExecute is invoked, the results must be delivered back to the Activity and displayed to the user: our AsyncTask needs a reference to the Activity (maybe we need a Context to start another Activity, or some widgets to display the results). If we use a regular (strong) reference, memory leaks may occur. Moreover, what happes if the Activity no longer exists because the user has closed the app?

The solution is using weak references. JVM ignores weak references. That means objects which have only week references are eligible for garbage collection. They are likely to be garbage collected when JVM runs the garbage collector thread. JVM doesn’t show any regard for weak references.
Using weak references doesn’t prevent the garbage collector from deleting unnecessary objects in memory. In the method onPostExecute, by invoking get() on the weak references, we check if the objects are still in memory or have already been garbage collected. Only in the first case we can display the results to the connected Activity.
Have a look at the following example (the parts of interest are highlighted):
public static class GetScheduledRecordingsTask extends AsyncTask> {
        @Inject
        DBHelper dbHelper;

        private final WeakReference weakFragment;
        private final WeakReference weakCalendarView;
        private final WeakReference weakCompactCalendarViewListener;

        private final Date selectedDate;

        public GetScheduledRecordingsTask(ScheduledRecordingsFragment scheduledRecordingsFragment, CompactCalendarView compactCalendarView, CompactCalendarView.CompactCalendarViewListener compactCalendarViewListener, Date selectedDate) {
            App.getComponent().inject(this);
            weakFragment = new WeakReference<>(scheduledRecordingsFragment);
            weakCalendarView = new WeakReference<>(compactCalendarView);
            weakCompactCalendarViewListener = new WeakReference<>(compactCalendarViewListener);
            this.selectedDate = selectedDate;
        }

        protected List doInBackground(Void... params) {
            return dbHelper.getAllScheduledRecordings();
        }

        protected void onPostExecute(List scheduledRecordings) {
            ScheduledRecordingsFragment scheduledRecordingsFragment = weakFragment.get();
            CompactCalendarView calendarView = weakCalendarView.get();
            CompactCalendarView.CompactCalendarViewListener compactCalendarViewListener = weakCompactCalendarViewListener.get();
            if (scheduledRecordingsFragment == null || calendarView == null || compactCalendarViewListener == null)
                return;

            calendarView.removeAllEvents();
            for (ScheduledRecordingItem item : scheduledRecordings) {
                Event event = new Event(ContextCompat.getColor(scheduledRecordingsFragment.getActivity(), R.color.accent), item.getStart(), item);
                calendarView.addEvent(event, false);
            }
            calendarView.invalidate(); // refresh the calendar view
            compactCalendarViewListener.onDayClick(selectedDate); // click to show current day
        }
    }

// ...

new GetScheduledRecordingsTask(scheduledRecordingsFragment, calendarView, compactCalendarViewListener, selectedDate).execute();

Thursday, 26 October 2017

Testing Service lifecycle method calls

Let's suppose you have created a local Service (implementing the Local Binder Pattern) to perform some tasks for your app, delivering the results back to a connected Activity. 
It is important to properly test the Service to make sure it behaves as you expect. The first thing to do, as the Android documentation suggests, is testing the Service's lifecycle methods.

To be more precise:
  • ensure that the onCreate() is called in response to Context.startService() or Context.bindService(). Similarly, you should ensure that onDestroy() is called in response to Context.stopService(), Context.unbindService(), stopSelf(), or stopSelfResult(). Test that your Service correctly handles multiple calls from Context.startService(). Only the first call triggers Service.onCreate(), but all calls trigger a call to Service.onStartCommand()
If you are not familiar with testing Services you can first check the official Android documentation here:
Testing Your Service


But how do you do that without mixing the code of your Service, implementing you business logic, with the code needed to keep track of the method calls?

@VisibleForTesting

The @VisibleForTesting annotation indicates that an annotated method (or variable) is more visible than normally necessary to make the method testable. This annotation has an optional otherwise argument that lets you designate what the visibility of the method should have been if not for the need to make it visible for testing. Lint uses the otherwise argument to enforce the intended visibility.

You can also specify @VisibleForTesting(otherwise = VisibleForTesting.NONE) to indicate that a method exists only for testing. This form is the same as using @RestrictTo(TESTS). They both perform the same lint check.
For our purpose we can define some static variables, inside our Service, used to keep track of the lifecycle method calls. These variables are annotated with @VisibleForTesting, meaning that they are only used for testing purposes.
// Just for testing.
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public static int onCreateCalls = 0;
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public static int onDestroyCalls = 0;
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public static int onStartCommandCalls = 0;
In the relevant Service lifecycle methods we increment our counters:
@Override
public void onCreate() {
    Log.d(TAG, "RecordingService - onCreate");
    onCreateCalls++;
    // ...
}

@Override
public void onDestroy() {
    Log.d(TAG, "RecordingService - onDestroy");
    onDestroyCalls++;
    // ...
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStartCommandCalls++;
    // ...
}

You can see the whole class on GitHub (it's a hybrid Service, both bound and started, used to record audio with a MediaRecorder):
RecordingService.java

Testing the Service

Testing our Service is now very easy. We first define the ServiceTestRule as follows:
@Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule() {
    @Override
    protected void afterService() {
        super.afterService();           
        RecordingService.onCreateCalls = 0;
        RecordingService.onStartCommandCalls = 0;
        RecordingService.onDestroyCalls = 0;
    }
};
Note that in the overriden afterService method, called when the Service is shut down after each test, we reset our counters to 0.

Testing the lifecyle method calls is now very easy:
/*
    Test that the Service's lifecycle methods are called the exact number of times in response
    to binding, unbinding and calls to startService.
*/
@Test
public void testLifecyleMethodCalls() throws TimeoutException {
    // Create the service Intent.
    Intent serviceIntent = RecordingService.makeIntent(InstrumentationRegistry.getTargetContext(), true);

    mServiceRule.startService(serviceIntent);
    IBinder binder = mServiceRule.bindService(serviceIntent);
    RecordingService service = ((RecordingService.LocalBinder) binder).getService();
    mServiceRule.startService(serviceIntent);
    mServiceRule.startService(serviceIntent);

    assertNotNull("Service reference is null", service);
    assertEquals("onCreate called multiple times", 1, RecordingService.onCreateCalls);
    assertEquals("onStartCommand not called 3 times as expected", 3, RecordingService.onStartCommandCalls);

    mServiceRule.unbindService();
    assertEquals("onDestroy not called after unbinding from Service", 1, RecordingService.onDestroyCalls);
}

You can have a look at the entire test class here:
RecordingServiceTest.java

Sunday, 14 May 2017

Scheduling repeated tasks offline at (almost) exact times

As you probably know, starting from API level 21 (Lollipo) the best way to schedule repeated tasks on Android is using:
  • the JobScheduler API (requires API level 21+);
  • the Firebase JobDispatcher, which provides a JobScheduler-compatible API that works on all recent versions of Android (API level 9+) that have Google Play services installed.
Running apps in the background is expensive, which is especially harmful when they're not actively doing work that's important to the user.
In recognition of these issues, the Android framework team created the JobScheduler. This provides developers a simple way of specifying runtime constraints on their jobs. Available constraints include network type, charging state, and idle state.

On the other hand, the Firebase JobDispatcher uses the scheduling engine inside Google Play services(formerly the GCM Network Manager component) to provide a backwards compatible (back to Gingerbread) JobScheduler-like API.

Problem

However, both the JobScheduler API and the Firebase JobDispatcher, in order to save battery life, depend on the mainance windows of Doze.
In other words, if a user leaves a device unplugged and stationary for a period of time, with the screen off, the device enters Doze mode. In Doze mode, the system attempts to conserve battery by restricting apps' access to network and CPU-intensive services. It also prevents apps from accessing the network and defers their jobs, syncs, and standard alarms.Periodically, the system exits Doze for a brief time to let apps complete their deferred activities. During thismaintenance window, the system runs all pending syncs, jobs, and alarms, and lets apps access the network.
Doze - mantainance windows

Warning

In this post we'll focus on scheduling repeated tasks (even offline, whithout an internet connection) that should start at (almost) exact times.

AlarmManager

Although the aforementioned solution is preferable, because it enables to save battery life, there might be scenarios in which you want to schedule a repetead task to run with an exact timing, even when the device is in Doze mode.
A possible solution is using the AlarmManager. The exact methods and the pattern to follow varies depending on the Android version, because:
  • Doze was first introduced with Marshmallow (API level 23);
  • the AlarmManager methods for scheduling tasks have slightly changed with API leve 19.

The following table shows the methods to use:



repeated
exact times
display ON
repeated
exact times
display OFF
up to API 18
setRepeating
RTC or ELAPSED_REALTIME
(1)
setRepeating
RTC_WAKEUP or
ELAPSED_REALTIME_WAKEUP
API 19-22
setExact with manual re-scheduling
RTC
ELAPSED_REALTIME
(1) (2)
setExact with manual re-scheduling
RTC_WAKEUP
ELAPSED_REALTIME_WAKEUP
API 23+
setExact or setExactAndAllowWhileIdle with manual re-scheduling
(3)
setExactAndAllowWhileIdle with manual re-scheduling
(3)

(1) If an alarm is delayed (by system sleep, for example, for non _WAKEUP alarm types), a skipped repeat will be delivered as soon as possible. After that, future alarms will be delivered according to the original schedule; they do not drift over time. For example, if you have set a recurring alarm for the top of every hour but the phone was asleep from 7:45 until 8:45, an alarm will be sent as soon as the phone awakens, then the next alarm will be sent at 9:00.
(2) as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact. With setExact the alarm will be delivered as nearly as possible to the requested trigger time.
(3) If you don't need exact scheduling of the alarm but still need to execute while idle, consider using setAndAllowWhileIdle(int, long, PendingIntent).

Frequency

For events less than 60 seconds apart, alarms aren’t the best choice: use the much more efficient Handler (http://goo.gl/CE9vAw) for frequent work.

setExactAndAllowWhileIdle: exact or not?

The method setExactAndAllowWhileIdle, used to trigger alarms even when the device is idle, can significantly impact the power use of the device when idle (and thus cause significant battery blame to the app scheduling them), so it should be used with care.

To reduce abuse, there are restrictions on how frequently these alarms will go off for a particular application:
  • Under normal system operation, it will not dispatch these alarms more than about every minute (at which point every such pending alarm is dispatched)
  • When in low-power idle modes this duration may be significantly longer, such as 15 minutes


Manual rescheduling

As you can see from the above table, starting with API 19 you have to use setExact or setExactAndAllowWhileIdle the schedule a repeated task at exact times.
Infact, as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described below. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.

Create an AlarmManager instance:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

Create an Intent to broadcast to a BroadcastReceiver:
Intent intent = new Intent(MyActivity.this, MyReceiver.class);

Create a PendingIntent that holds the intent:
PendingIntent pendingIntent = PendingIntent.getBroadcast(MyActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

Create the alarm:
alarmManager.setExact(AlarmManager.RTC_WAKEUP, initialDelay, pendingIntent);

Reschedule the next alarm in the onReceive method of your BroadcastReceiver using the same code described above.

WakefulBroadcastReceiver

The Alarm Manager holds a CPU wake lock as long as the alarm receiver's onReceive() method is executing. This guarantees that the phone will not sleep until you have finished handling the broadcast. Once onReceive() returns, the Alarm Manager releases this wake lock. This means that the phone will in some cases sleep as soon as your onReceive() method completes. If your alarm receiver called Context.startService(), it is possible that the phone will sleep before the requested service is launched. To prevent this, your BroadcastReceiver and Service will need to implement a separate wake lock policy to ensure that the phone continues running until the service becomes available.

An easy way to achieve this is by using a WakefulBroadcastReceiver. WakefulBroadcastReceiver is a BroadcastReceiver that receives a device wakeup event and then passes the work off to a Service, while ensuring that the device does not go back to sleep during the transition. This class takes care of creating and managing a partial wake lock for you; you must request the WAKE_LOCK permission to use it.

public class MyWakefulBroadcastReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Reschedule the alarm. See the code above.

        // Create an Intent to start a Service.
        Intent service = new Intent(context, MyService.class);

        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, service);
    }
}

The service (in this example, an IntentService) does some work. When it is finished, it releases the wake lock by calling completeWakefulIntent(intent). The intent it passes as a parameter is the same intent that the WakefulBroadcastReceiver originally passed in.

public class MyService extends IntentService {
    public MyService() {
        super("MyService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Do some work here.
        // At this point SimpleWakefulReceiver is still holding a wake lock
        // for us.  
        // Note that when using this approach you should be aware that if your
        // service gets killed and restarted while in the middle of such work
        // (so the Intent gets re-delivered to perform the work again), it will
        // at that point no longer be holding a wake lock since we are depending
        // on SimpleWakefulReceiver to that for us.  If this is a concern, you can
        // acquire a separate wake lock here.

        // Release the wake lock after executing the task.
        SimpleWakefulReceiver.completeWakefulIntent(intent);
    }
}

Saturday, 6 May 2017

Sticky Broadcast


Sticky Broadcast(s) are a special kind of Broadcast(s) that remain active for awhile after being sent. You can send a sticky Broadcast with the method:
Context.sendStickyBroadcast()

If you register a BroadcastReceiver that listens to sticky Broadcast(s), it can receive two kind of Broadcast(s):
  1. Broadcast(s) that have just been sent
  2. Broadcast(s) that have been sent previously and are still active
You can find out what kind of Broadcast you have just received with the method:
isInitialStickyBroadcast()
returning false if the Broadcast represents an event that has just occured.

Sticky Broadcast(s) are generally used for events lasting for a certain period of time. Examples of such Broadcast(s) sent by the system are:
  • Intent.ACTION_BATTERY_CHANGED: indicates changes in the battery level. BroadcastReceiver(s) must be programmatically registered with Context.registerReceiver()
  • Intent.ACTION_DOCK_EVENT: indicates when a device is placed on a dock
  • Intent.ACTION_DEVICE_STORAGE_LOW: indicates low memory condition
public class MyBroadcastReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(action.equals(Intent.ACTION_DOCK_EVENT)) {
            if(!isInitialStickyBroadcast()) {
                // this is a new broadcast
            } 
            else {
                // this is an old broadcast
            }
        }
    }
}

In order to send sticky Broadcast(s) you must include the permission android.permission.BROADCAST_STICKY in the Manifest file.

If you want to remove a sticky Broadcast you can use the method:
Context.removeStickyBroadcast(Intent intent)
passing the Intent that was previously broadcast.

NB. Sticky Broadcast(s) consume system resources, so use them sparingly when really needed!

API level 21

Starting from API level 21 sticky Broadcast(s) are deprecated.
"Sticky broadcasts should not be used. They provide no security (anyone can access them), no protection (anyone can modify them), and many other problems. The recommended pattern is to use a non-sticky broadcast to report that something has changed, with another mechanism for apps to retrieve the current value whenever desired".

Monday, 26 September 2016

Reverse engineering part 2: apktool

After getting the apk file (see article: Reverse engineering part 1: getting the apk file) it's time to disassemble it and analyze its content.
The tool I will talk about is called apktool and can be downloaded from https://ibotpeaches.github.io/Apktool/, where you can also read the complete documentation. Apktool is especially useful to analyze resource files (xml layout files, Manifest, drawables, xml animation files, strings).

Apktool

Apktool is a tool for reverse engineering 3rd party, closed, binary Android apps. It can decode resources to nearly original form and rebuild them after making some modifications. It also makes working with an app easier because of the project like file structure and automation of some repetitive tasks like building apk, etc.

Reverse engineering

After installing apktool you'll find the apktool.bat file that can be run from the command line.
You just have to type:
apktool d filename.apk
(replace filename.apk with the actual file name of the apk file)

and apktool disassembles the apk file in a folder where you can find all the resource files perfectly readable.

For example, I ran apktool for Whatsapp and this is the result:

In the "res" folder you can have a look at almost every resource file of the app:


This is the layout file settings_security.xml of Whatsapp. As you can see the file is completely readable.

This is another example: the animation defined in design_fab_out.xml, that fades out a FloatingActionButton:
Analyzing the file you can find out how the animation is achieved: animating alpha from 1 to 0 and the scale of the FAB from 1 to 0. Very easy!

Conclusion

Apktool is a very powerful utility especially if you want to analyze the resource files of an app. If you have found an app with a beautiful design, or a fancy animation, and want to find out how they are made, apktool is the tool to use.