Building Android preference screen

Some days ago, I started building a Setting Screen for my Android app. Everything was fine, until I opened it on an older Android version. The overview screen had no material design, a thing I would have accepted, if there weren’t those completely destroyed dialogs: Android’s internal preferences are using Android’s internal app.AlertDialogs. Those dialogs in combination with the AppCompat Dialog Theme, which I had applied to them, resulted in a dialog with two frames on older devices (One system default dialog and a material frame around it). So I decided to switch to the android.support.v7.preference library, only to face a lot more issues. Including the Library In order to use the new preferences, we need to import a library. To do so, we add this line to our gradle dependencies (You should change the version number to the latest). compile 'com.android.support:preference-v7:23.4.0' Building The Preference Screen Creating the Preferences At first, we need to create our preference structure: We create a new XML Android resource file as xml/app_preferences.xml. Now we can add our preference structure to this file. Make sure to add a unique android:keyattribute for each preference. More information: How to build the XML <android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <android.support.v7.preference.PreferenceCategory android:title="Category 1"> <android.support.v7.preference.SwitchPreferenceCompat android:key="key1" android:title="Switch Preference" android:summary="Switch Summary" android:defaultValue="true" /> <android.support.v7.preference.EditTextPreference android:key="key2" android:title="EditText Preference" android:summary="EditText Summary" android:dialogMessage="Dialog Message" android:defaultValue="Default value" /> <android.support.v7.preference.CheckBoxPreference android:key="key3" android:title="CheckBox Preference" android:summary="CheckBox Summary" android:defaultValue="true"/></android.support.v7.preference.PreferenceCategory></android.support.v7.preference.PreferenceScreen> The v7.preference library provides some preferences we can use: CheckBoxPreference, SwitchPreferenceCompat, EditTextPreference and a ListPreference (and a basic Preference). If we need more than these predefined preferences, we have to build them on our own. Creating the Preference Fragment Now we need to create our Preference Fragment, where we can show the preferences from our XML file. We do this by creating a new class, called SettingsFragment, which extends PreferenceFragmentCompat. Since the onCreatePreferences is declared as abstract in the source code of the library, we are forced to include our own implementation to tell the fragment to load our just created app_preferences.xml. import android.support.v7.preference.PreferenceFragmentCompat; public class SettingsFragment extends PreferenceFragmentCompat { @Override public void onCreatePreferences(Bundle bundle, String s) { // Load the Preferences from the XML file addPreferencesFromResource(R.xml.app_preferences); } } We can add this SettingsFragment (v4.app.Fragment), like any other Fragment (e.g. with a FragmentTransaction) to an Activity. Applying the Preference Theme Finally we need to specify a preferenceTheme in our Activity’s theme. If we don’t do so, the App will crash with an IllegalStateException. The v7.preference library provides only one Theme: PreferenceThemeOverlay(You may have a look at its source code). We add this with the following line in our Activity’s theme: <item name="preferenceTheme">@style/PreferenceThemeOverlay</item> After we have done this, our result should now look like this. (The Activity’s parent theme is Theme.AppCompat and the background is set with android:windowBackground) Settings Screen with PreferenceThemeOverlay As you can see, it has an oversized font and a horizontal line below the category title. This is definitely not material design. It more looks like a mixture of material design for the CheckBoxand Switch widgets on the right and an old…

Continue ReadingBuilding Android preference screen

Motion in android

So earlier this year I attended a talk where the speaker wanted to introduce us to meaningful motion in android apps and he convinced us to use this in our apps as well. Motion came in with Material design, actually not really came but became popular with Material design and since google has added the same kind of motions to their apps as well, developers have started using it. I love motion, not only does it boost engagement but it’s instantly noticeable. Think of the apps you use that feature motion design and how pleasing, satisfying, fluent and natural they feel to experience. Eg. Zomato, Play music etc. Now think of some apps that don’t use any kind of motions and you’ll realise they look a bit boring and you as users will always prefer apps with some kind of motion. Touch So firstly let’s discover the feedback on touch. It helps to communicate to the user in a visual form that some interaction has been made. But also keep in mind that this animation should be enough for them to gain clarity and encourage further explorations and not distract them. For adding backgrounds you can use the following : ?android:attr/selectableItemBackground — Show a ripple effect within the bounds of the view. ?android:attr/selectableItemBackgroundBorderless — Show a ripple effect extending the bounds of the view. View Property Animator Introduced in API 12, this allows us to perform animated operations (in parallel) on a number of view properties using a single Animator instance Some of the parameters that can be added to a view are as follows : alpha() -Set the alpha value to be animated to scaleX() & scaleY() — Scales the view on it’s X and / or Y axis translationZ() — Translates the view on its Z axis setDuration() — Sets the duration of the animation setStartDelay() — Sets the delay on the animation setInterpolator() — Sets the animation interpolator setListener() — Set a listener for when the animation starts, ends, repeats or is cancelled. Now let’s write some code on how to do this on a button for example: mButton.animate().alpha(1f) .scaleX(1f) .scaleY(1f) .translationZ(10f) .setInterpolator(new FastOutSlowInInterpolator()) .setStartDelay(200) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }) .start(); Note : Use ViewCompat class to implement the ViewPropertyAnimator from Android API version 4 and up Object Animator Similar to the ViewPropertyAnimator, the ObjectAnimator allows us to perform animations on various properties of the target view (both in code and XML resource files). However, there a couple of differences: The ObjectAnimator only allows animations on a single property per instance e.g.Scale X followed by Scale Y. However, it allows animations on a custom Property e.g. A view’s foreground colour. Her we need to set the evaluator, set the delay and call start(). private void animateForegroundColor(@ColorInt final int targetColor) { ObjectAnimator animator = ObjectAnimator.ofInt(YOUR_VIEW, FOREGROUND_COLOR, Color.TRANSPARENT, targetColor); animator.setEvaluator(new ArgbEvaluator()); animator.setStartDelay(DELAY_COLOR_CHANGE); animator.start();} Interpolators An Interpolator can be used to define the rate of change for an animation, meaning the speed,…

Continue ReadingMotion in android

Flavors in Gradle

Ever wondered how do people maintain different versions of the same app on play store with some customisations in each version. For example, Lite version and a pro version which signify free and paid versions with extra features in paid one. With the arrival of gradle as a build tool, we got gradle flavors which is an excellent way to have some variations in your app versions. It can also be leveraged to do hermetic testing with prod/mock flavors. Some other examples could be free/paid flavors and stable/experimental flavors etc. Now let’s talk about on how to proceed : The process is very simple Create the flavors in you build.gradle file android { productFlavors { mock { applicationIdSuffix = ".mock" } prod } } Here prod and mock are two flavors and the customisations we add are that mock has a applicationIdSuffix. We can also add altogether different applicationIds as well as define a different version for different flavors and a lot of other different things 2. Now we sync the project If you now open Build Variants tool window either using the quick access menu located in the status bar in the bottom left hand corner of the Android Studio main window or using the Build Variant tool window bar. Once loaded, clicking in the Build Variant cell for the app module should now list the four build variants: mockDebug, mockRelease and prodDebug, prodRelease After this we get different folders corresponding to the different flavors. We go ahead and add the different folders where we want the changes to occur. For example if I want to show two different maps for two different versions, where one version is for fdroid and another one is for googleplay and hence we replace google maps with OpenStreet Maps in the fdroid version. Hence, We add res/values and res/layout folders for different resources and we also add different java folders and add the classes that would be different for both the flavors. Since both of these will require different set of permissions we can also add AndroidManifest.xml for these were we define the permissions etc. we want. Not that we only add the changes for each file. All the other essential things can be added from the Main flavor of the android project. Any variable available through your code Another thing to know is that the you can have add variables like this so that you can use different variables for different flavors. For examle if you have different api’s and different options for each flavor like if you want to report crashes in one of the flavors and not in other. Here HOST variable is not the only one you can expose in your code. You can do it with whatever you want: prod { applicationId "zuul.com.android" buildConfigField 'String', 'HOST', '"http://api.zuul.com"' buildConfigField 'String', 'FLAVOR', '"prod"' buildConfigField "boolean", "REPORT_CRASHES", "true" } You can access them as follows: BuildConfig.HOST BuildConfig.FLAVOR BuildConfig.REPORT_CRASHES So I think this is a really useful thing to know and is used…

Continue ReadingFlavors in Gradle

Using the new Awareness API

Google released the new Awareness API for everyone, a suite of signals concurring to gives the developer the context in which our user is, all as part of the Play Services 9.2.0, already available on Android devices around the globe. This library combines 7 different feeds managing both the battery drain and the used memory, providing us with data such as user location, weather, time, headphones connection status, places, nearby beacons and currently performing activity. INTRO The Awareness API comes with two different versions, one is a callback based one (Fence API) and the other one is a polling based one (Snapshot API): while the first one will notify us when the conditions we specified are met, the second expects us to query the suite for the data from a unified interface. There are 7 kinds of context that we can work with in Awareness API such as Time — Local time at current user’s location Location — Latitude/Longitude Place — Places around user Activity — Detected user activity (biking, walking, running, etc.) Beacons — Check nearby beacon(s) Headphones — Are headphones plugged in? Weather — Current weather conditions Now the Two set’s of API’s : Snapshot API — Allows you to “request an information based on user’s context” as listed above. Fence API — Allows you to “receive a signal when user’s context has changed and reaches the condition” through callback function, for example, if user moves closed to the specific coordinate with headphones plugged in, Fench API will call the registered BroadcastReceiver and let you do your job. Getting started Create a project in https://console.developers.google.com (or in case you already have one, you can use it instead) And then browse to API Manager page and search for Awareness and click at Awareness API and Click Enable and wait until it finishes enabling 3. Go to Credentials tab and click at Create credentials -> API key -> Android key. Enter the name you project, for example, Android key and click Create (or if you have already created Android key previously, you could skip this step and use the existed one) 4. Add dependency in build.gradle compile 'com.google.android.gms:play-services-contextmanager:9.2.0' 5. Open AndroidManifest.xml file and add meta-data to <application> tag like this: <meta-data android:name="com.google.android.awareness.API_KEY" android:value="YOUR_KEY" /> <meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_KEY" /> <meta-data android:name="com.google.android.nearby.messages.API_KEY" android:value="YOUR_KEY" /> We also need to add permissions for this: 6. Open AndroidManifest.xml <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" /> Now we come to the actual java code. open your activity and initialise the GoogleApiClient in onCreate() private GoogleApiClient mGoogleApiClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_layout); mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Awareness.API) .build(); mGoogleApiClient.connect(); } So this ends the setup part. Now we move on to the actual data retrieval from the API. Accessing Snapshot API Awareness.SnapshotApi.getDetectedActivity(mGoogleApiClient) .setResultCallback(new ResultCallback<DetectedActivityResult>() { @Override public void onResult(@NonNull DetectedActivityResult detectedActivityResult) { if (!detectedActivityResult.getStatus().isSuccess()) { Log.e(TAG, "Could not get the current activity."); return; } ActivityRecognitionResult ar = detectedActivityResult.getActivityRecognitionResult(); DetectedActivity probableActivity = ar.getMostProbableActivity(); Log.i(TAG, probableActivity.toString()); } }); This will return the most probable activity done by the user. We can get a particular activity like walking, running, driving as well by integers defined below public…

Continue ReadingUsing the new Awareness API

Bottoms sheets in android

Hey Guys I recently used Bottom sheets, so that I should write a blog about it because I don’t see a lot of developers using this in their app UI’s. It’s a very interesting UI element. A Bottom Sheet is a sheet of material that slides up from the bottom edge of the screen. Displayed only as a result of a user-initiated action, and can be swiped up to reveal additional content. It can be a temporary modal surface or a persistent structural element of an app. This component was introduced in the Android Design Support library 23.2. Many apps like Google Maps use the bottom sheet, in which a sliding window pops up from the bottom of the screen. Also the Google play music app uses. When we drag up the sheet we see the song details as well as the current playlist. Usage of expanded and collapsed Bottom sheets in Android There are 3 states of Bottom sheets :- Expanded — When the sheet is completely visible. Collapsed — When the sheet is partially visible. Hidden — When the sheet is completely hidden. Step 1 is we need to import the latest design support library. Put this line in your app level build.gradle file. compile ‘com.android.support:design:23.2.0’ Then one should create a new Blank Activity (not Empty Activity) in Android Studio. It sets up the CoordinatorLayout by default. So now there ate two layouts created by default namely activity_main.xml and content_main.xml. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="yet.best.bottomsheets.MainActivity" tools:showIn="@layout/activity_main"> <Button android:id="@+id/open" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_margin="5dp" android:text="Open Bottom Sheet" /> <Button android:id="@+id/collapse" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/open" android:layout_centerHorizontal="true" android:layout_margin="5dp" android:text="Collapse Bottom Sheet" /> <Button android:id="@+id/hide" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/collapse" android:layout_centerHorizontal="true" android:layout_margin="5dp" android:text="Hide Bottom Sheet" /> </RelativeLayout> Notice that 3 Buttons have been created in this layout to perform different actions with the bottom sheets. activity_main.xml <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="yet.best.bottomsheets.MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_main" /> <include android:id="@+id/bottom_sheet" layout="@layout/bottomsheet_main" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_dialog_email" app:layout_anchor="@id/bottom_sheet" app:layout_anchorGravity="top|right|end" /> </android.support.design.widget.CoordinatorLayout> Those who aren’t familiar with the coordinator layout — basically there is a base level layout activity_main which contains the FloatingButton and within this layout including content_main.xml which will contain the rest of the layout. Notice that one also has to include bottomsheet_main.xml. This layout contains our bottom sheet layout which will be created next. Create a new layout file called bottomsheet_main.xml bottomsheet_main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="300dp" android:background="#d3d3d3" app:behavior_hideable="true" app:behavior_peekHeight="70dp" app:layout_behavior="@string/bottom_sheet_behavior"> <TextView android:id="@+id/heading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="7dp" android:text="Collapsed" android:textSize="18sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Main Content" android:textSize="20sp" /> </RelativeLayout> This layout is how our bottom sheet will actually look. You can design it as you want. Now for the actual java code. This is the easiest part actually. Just set listeners to the 3 buttons created and perform the corresponding action with the bottom sheet. public class MainActivity extends AppCompatActivity { BottomSheetBehavior bottomSheetBehavior; Button open, collapse, hide; TextView heading; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar =…

Continue ReadingBottoms sheets in android

Handling data in android

So this week I was working with getting some data from the sqlite database in android and someone who was a beginner in android also asked me to help him with the same. I asked him what he knew and he said that even after reading up a lot he wasn’t able to figure out what exactly to do with the data he wants to save and use in his app. I have seen that this is a problem with a lot of people starting to develop android apps. So, I decided to write a blog on how can you handle your data in your android apps. Most of the android apps need to save data even if only to save some user preferences. So primarily there are 3 ways to save your data : In form of key values (SharedPreferences) Reading/Writing to files Writing to a database So let’s go step by step. When we need to store just some preferences of the users like if they want notifications or what kind of theme they want : light or dark etc. So basically if we want to store a key value in the persitant storage of the app we can do that using SharedPreferences. To use sharedpreferences, we initialise the sharedpreference object like SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); in oncreate and cache it. Then we just need to add or retrieve what we want using this cached SharedPreferences object. To Add a key value pair : sharedPreferences.edit().putString("someKey", "someValue").apply(); Also you can put all kinds of stuff here. For example right now we added a string with key “someKey” and Value “someValue”. We can also add Booleans, Floats, Ints, Longs, StringSets etc. To retrieve the same value we do something like this sharedPreferences.getString("someKey", "DefaultValue"); Now if you want some logs or some values that can be exported and sent to your server, you should write them to files and maybe read some json inputs etc. as well. Basically android has a File system similar to other platforms. All android devices have two file storage areas : “Internal” and “external” storage. The difference between them is as follows : Internal : Always available Files saved here are accesible by only your app When user uninstalls the app, system removes all your app’s files from internal storage Best to use this when you want to be sure that neither the user nor the other app’s can access your files External : It’s not always available because user can mount external storage as USB storage and remove it as well It’s readable by anything(Other apps, users etc.) When you uninstall, system removes your app’s files from here only if you save them in the directory from getExternalFilesDir() Now to read and write files, you need extra permissions android.permission.WRITE_EXTERNAL_STORAGE android.permission.READ_EXTERNAL_STORAGE So now let’s get down to it. How do I save and read files in my app? You first initialise the File object File file = new File(context.getFilesDir(), filename); This will create a file with filename in the internal storage. For external…

Continue ReadingHandling data in android

UI Testing in Android

Testing in android is something that most people simply don’t do. When I first started with developing android apps, I followed the same dev cycle : Develop a feature deploy to a device manual testing for bugs and errors fix these issues due to wrong implementation then again deploy and manually test and so on…… Trust me it’s a tedious process and no one actually can test for the corner cases that may arise. You may be able to cover like 85% of the cases for each feature but when there are a ton of features you’ll collectively cover much less than 85% as well. So, We should definitely write tests for android. I’ll start with Espresso testing first. Quote from the Android developer website: “Testing user interactions within a single app helps to ensure that users do not encounter unexpected results or have a poor experience when interacting with your app. You should get into the habit of creating user interface (UI) tests if you need to verify that the UI of your app is functioning correctly. The Espresso testing framework, provided by the Android Testing Support Library, provides APIs for writing UI tests to simulate user interactions within a single target app. Espresso tests can run on devices running Android 2.2 (API level 8) and higher. A key benefit of using Espresso is that it provides automatic synchronization of test actions with the UI of the app you are testing. Espresso detects when the main thread is idle, so it is able to run your test commands at the appropriate time, improving the reliability of your tests. This capability also relieves you from having to adding any timing workarounds, such as a sleep period, in your test code. The Espresso testing framework is an instrumentation-based API and works with theAndroidJUnitRunner test runner.” Getting started We first need to setup espresso in android studio. That can be done by adding this to the app level build.gradle dependencies { ... androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1' } After adding this dependency and setting up testing on android, now we can move on to write some tests. We start by adding a new class in androidTest where we will actually write the tests. There are 3 parts to writing an espresso test : ViewMatchers: Find a view to act/assert upon something ViewActions: something to perform an action(click, type etc.) ViewAssertion: something to verify what you expect Components For writing tests in espresso we just have to use these three parts to see if the ui is working as it should. Let’s say we want to type some text in an edittext and see if the text we have typed is as we expect it to be. To start, we’ll see how we use viewMatchers. We need to find a view in ViewMatchers. For that, we will do something like this withId(R.id.edittext_id) Now we perform a click on this eddittext perform(typeText("Hello")) Now we join the two by wrapping in onView() onView(withId(R.id.edittext_id)).perform(typetext("Hello")) This will just type “Hello” in the…

Continue ReadingUI Testing in Android

Working with Styles and Themes in Android

All those who have worked with styles and themes know that they’re hard to get right. We tend to get frustrated when we work with them. The hierarchy easily devolves into spaghetti code. How often did you want to change a style but feared you might break the continuity of the design of the app somewhere or the other. I ran into a similar situation recently. I had to change the whole app’s style’s and theme by just changing the colors etc. in one location. This was for the Open Event android project where we wanted that while generating an apk by the apk generator we could change the color scheme of the app and could make it customisable for the needs of the organisations. So, I’ll be talking about styling different views in this post. This shall be a long post! When should we use styles First of all, most of us get confused on when should we use styles instead of an inline attribute. Now I am going to show the rules that I follow: When you have multiple views that should look identical ( Perhaps that do similar things) Few Examples : Payment screens. You want to get the user through a bunch of ordering and payment screens. You need similar kind of buttons there to make it look like a continuous process. Hence we make the Buttons follow one particular style <style name="Payment_Buttons"> <item name="android:minWidth">@dimen/button_min_width</item> <item name="android:minHeight">@dimen/button_min_height</item> <item name="android:background">@color/my_choice_color</item> </style> Try to use themes to tweak default styles Themes provide a way of defining the default style of many widgets. For example : If you want to define the default button for all of your payment screens in the example above, you can do something like : <style name="ButtonTheme"> <item name="android:buttonStyle">@style/MyButton</item> </style> But note that if you’re tweaking the default style, the only tricky part is to figure out the parent of your style but that’s really dificult due to a lot of variation within the different versions of android. If you’re using something that’s part of the AppCompat, then it’s okay. you don’t need to worry about the variations but when you want to style something not in AppCompat, then the main problem arises. So For example I want a button to be Holo until kitkat and then Material starting Lollipop, I’ll do something like this : In values/styles.xml - <style name="ButtonParent" Parent = "android:Widget.Holo.Button" /> <style name="ButtonParent.Holo"> <item name="android:background">@drawable/my_bg</item> </style> Then in values-v21/styles.xml: <style name="ButtonParent" parent ="android:Widget.Material.Button/> This makes the button consistent with guidelines and the app looks perfect. Now, Themes vs Styles This is a topic which most of the developers don’t know about. They get confused on what is the difference between them. I was also not totally clear about this until recently. A theme is infact a style, the only difference is the usage. We set a theme in the Manifest of the app or an activity We set a style in a layout file or a widget There are more styles than themes (Checkout…

Continue ReadingWorking with Styles and Themes in Android

Uploading json assets and icons to your app via the app generator

If you have tried out our app generator webpage, you should’ve noticed an option that allows you to upload a zip file which will contain the json for the event. Why do we need this? Well, this is needed because not every event organizer can maintain a server and API endpoints which contain the details for their event, so they can simply generate a json for the event by exporting it through the options provided to them on Google Spreadsheets and then they can upload them on the server, so that these files can be packaged in the android and web apps. Implementation : The implementation is pretty straightforward and consists of 3 parts : Making changes to the html file to allow user to upload the zip <tr> <td valign="top"> <label for="sessions">Zip containing .json files</label> </td> <td valign="top"> <input accept=".zip" type="file" id="uploadZip" name="sessions"> </td> </tr> 2. Retrieve this file in the javascript and then make an AJAX call to the server var file_data = $('#uploadZip').prop('files')[0]; var form_data = new FormData(); form_data.append('file', file_data); $.ajax({ url: '/upload.php', // point to server-side PHP script cache: false, contentType: false, processData: false, data: form_data, type: 'post', success: function(php_script_response){ // do something } }); So here, the form_data contains the details about the file to be uploaded. In the AJAX call, upload.php takes reads form_data variable and then initiates the upload to the server. 3. Setup a PHP script on the server to respond to the above AJAX call <?php if ( 0 < $_FILES['file']['error'] ) { echo 'Error: ' . $_FILES['file']['error'] . '<br>'; } else { move_uploaded_file($_FILES['file']['tmp_name'], "/var/www/html/uploads/upload.zip"); } ?> Here in the PHP script, the file is read and uploaded to a temporary directory in the server. We then manually copy it to a location and name of our choice. In case there are multiple users accessing the website and uploading their assets at the same time, we need to pass a timestamp variable to the AJAX call to and later on use it while renaming the uploaded file. This is to ensure that the file uploaded by one user is not overwritten by another user. How do we use this data during app compilation The uploaded zip is then uncompressed and its contents are moved to the assets folder of the android app’s directory. zip_ref = zipfile.ZipFile(path_to_zip_file, 'r') zip_ref.extractall(directory) zip_ref.close() #TODO: Change path here for f in os.listdir(directory+ "/zip"): if f.endswith('.json'): copyfile(f, directoy + "open-event-android/android/app/src/main/assets/"+f) elif f.endswith('.png'): copyfile(f, directory + "open-event-android/android/app/src/main/res/drawable"+f) replace(directory+"/open-event-android/android/app/src/main/res/values/strings.xml", 'mipmap/ic_launcher', 'drawable/' + f) Here replace is a function that searches in the source file for the phrase supplied as it’s argument and changes it with the new phrase. Now when the app is compiled and ran on a user’s device, it will first search for a json file in the assets directory and if it exists, use that for fetching the data instead of making a network call. For this I have used Gson to first parse the offline files otherwise retrofit makes request to the api and fetches the data…

Continue ReadingUploading json assets and icons to your app via the app generator

How to parse json assets with gson

So most of us have json assets in our app which we parse on runtime to get the data and use it accordingly but what I have seen is that most of the people create a JSONObject or JSONArray after reading the json into an inputstream but then handling it becomes difficult since we have to manually extract every entity in each array which makes it bound to a lot of errors. A better approach to using it is making use of gson : an open source library by Google to serialise and deserialise Java object to (and from) Json. It’s pretty easy to use and makes the development process easy. For those of you not still convinced on using gson, I’d like to demonstrate the code we had to write to without using gson and the one using gson as well. So to start with lets see the json file we’ll be using. It’s the events.json file from the open event project. { "events": [ { "color": "#fdfdfd", "email": "dev@fossasia.org", "end_time": "2015-07-14T00:00:00", "id": 4, "latitude": 37.783839, "location_name": "Moscone centre", "logo": "http://mysecureshell.readthedocs.org/en/latest/_images/logo_redhat.png", "longitude": -122.400546, "name": "FOSSASIA", "slogan": "Fossasia", "start_time": "2015-05-28T13:00:00", "url": "www.google.com" } ] } As you can see it has an object that has an array of event objects. So what we’ll first do is that we’ll get the whole json as a string by openeing an inputstream and then directing it to a buffer. Then we convert the buffer array to a string object. String json = null; try { InputStream inputStream = getAssets().open("events.json"); int size = inputStream.available(); byte[] buffer = new byte[size]; inputStream.read(buffer); inputStream.close(); json = new String(buffer, "UTF-8"); } catch (IOException e) { e.printStackTrace(); } Now we have the json as a string which we can now parse it using a combination of JSONObject and JSONArray. First we’ll access data in the outer json object i.e. “events”. That’ll be done by JSONObject jsonObject = new JSONObject(json); JSONArray events = jsonObject.getJSONArray("events"); Now that we have the array, we can traverse it to get the objects inside events array for (int j=0; j < events.length(); j++){ JSONObject cit = events.getJSONObject(j); String color = jsonObject.getString("color"); String email = jsonObject.getString("email"); String endTime = jsonObject.getString("end_time"); String id = jsonObject.getString("id"); String latitude = jsonObject.getString("latitude"); String locationName = jsonObject.getString("location_name"); String logo = jsonObject.getString("logo"); String longitude = jsonObject.getString("longitude"); String name = jsonObject.getString("name"); String slogan = jsonObject.getString("slogan"); String startTime = jsonObject.getString("start_time"); String url = jsonObject.getString("url"); } This is how we go about it. Now for the exiting part. We already have an Event data class which has the constructor, getters and setters etc. public class Event { int id; String name; String email; String color; String logo; @SerializedName("start_time") String start; @SerializedName("end_time") String end; float latitude; float longitude; @SerializedName("location_name") String locationName; String url; String slogan; public Event(int id, String name, String email, String color, String logo, String start, String end, float latitude, float longitude, String locationName, String url, String slogan) { this.id = id; this.name = name; this.email = email; this.color = color; this.logo = logo; this.start =…

Continue ReadingHow to parse json assets with gson