Tumblelog by Soup.io
Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

July 17 2017

07:11
Ron English Make America GRIN Again Pop-Up During SDCC Weekend (7.19 7.22)
05:58
Jermaine Rogers at SDCC A New Treasure Trove of Toys

July 16 2017

16:09
1470- Nova Fexerj, Iago, Freud, Federer e Spraggett. Voltei!!

July 15 2017

06:41
Super7s Skeletors Lair UNKLE Pop-Up Shop
06:23
Super7 San Diego Comic-Con Update!

July 14 2017

13:00

Android O: Phone Number Verification With SMS Tokens

In this tutorial, you'll learn about the cool new SMS token feature in Android O. You'll learn how to generate an app-specific token that will fire up a pending intent when the device first receives a message containing that token so as to verify the phone number. 

Kindly note that as of this writing, the Android O APIs are still in their third developer preview, though they are final. (We still don't know what O stands for yet!)

What Is an SMS Token?

An SMS token or one-time password is a security mechanism used to authenticate or verify a user. The user enters their phone number, and a limited lifespan token is generated specifically for that user. The user then receives the token as an SMS to their phone. In the case of Android O as of this writing, this app-specific token does not expire, but instead becomes invalid when another is generated. 

Why Use Android O's SMS Token?

One of the major reasons you might consider using Android O's SMS token mechanism is that it improves the user experience of the app. The user does not need to copy and paste the token from the SMS client to be verified. Instead, the Android device automatically detects the token sent to the user's device and then triggers the app component in the intent associated with your app (we'll get to that shortly). 

Even better, this functionality doesn't require the READ_SMS permission or any other. This mechanism also improves the security of your app user, because no other app can read the message containing the token on the device. 

In this tutorial, you'll learn how to use this new feature in Android O's SMS API. You'll learn how to generate an SMS token specifically for your app and send it to a device. We'll use the Android Studio emulator to simulate this process. 

Prerequisites

To follow along with this tutorial, make sure you have downloaded the Android 8.0 (O) SDK platform on your computer and have an emulator already set up that targets this version. 

1. Generate the App-Specific Token

To start off, I'll show you how to generate an app-specific token which is unique to your app on the user's device.

Create a New Android Studio Project

Fire up Android Studio and create a new project with an empty activity called MainActivity.

Create a new Android Studio project

Modify the build.gradle File

Make the following changes to your app module's build.gradle file. 

Modify the MainActivity Class

In the code snippet below, we get the SMSManager class and then call the method createAppSpecificSmsToken(). This does just what it says—it creates the app-specific SMS token. This method requires a PendingIntent which contains the Activity to be fired up when an SMS containing this token (a string 11 characters long) is received by the device. 

Be aware that, as stated earlier, the generated token is unique to your app on the user's device. If you create another SMS token, the second one will be a valid token while the first one will be ignored. 

Lay Out the Screen

Here's a MainActivity layout file set up to display the SMS token that was generated:

Running the app at this point will show the SMS token generated. 

Running the app at this point

2. Receive the SMS Token

Next, we'll create the activity to be fired up when our device receives a message containing the SMS token. Nothing specific to SMS tokens happens here. 

Lay Out the Screen

Here we create the layout for the activity we created above that contains just one TextView

Next, we'll test this functionality using the Android Studio emulator.

3. Test the SMS Token

Set Up the Emulator to Send SMS Messages

You can use your emulator to simulate receiving an SMS message, but you'll need to do a little setup. Open your emulator, click the last button on the right-side navigation bar to open the extended control dialog, and then select the phone control button. 

Showing how to use the Emulator to simulate sending an SMS message

From this interface, you can simulate your device receiving a phone call or SMS from another phone.

Send the Token

Make sure you have set up your emulator that targets Android 8.0 (O). Generate a token and enter a text message that contains it. Then click the Send Message button.  

Sending a message with emulator

Finally, the activity we specified in the pending intent gets fired up immediately! Try sending the message again and see that this time, it will show up in the device SMS client instead, because it is no longer a valid token. 

Token result activity

Using a Server

For a production app, the SMS token will typically be sent by a back-end server. So when using a server, the client (your app) should make a request to the server, including the app generated token and the phone number. Your server will then receive this request and send the unmodified token back as a text message to the user's phone. Your app will then receive this token and fire up the component registered in the pending intent. That component can then let the server know that the phone number verification or user authentication succeeded.

Conclusion

In this tutorial, you learned about the awesome SMS token feature introduced in Android O: what is it, its benefits, and how to use it in an Android app. 

To learn more about Android SMS and phone APIs, including how to make calls from your app, check out my related tutorial here on Envato Tuts+.

  • Android SDK
    How to Make Calls and Use SMS in Android Apps
    Chike Mgbemena

And check out some of our other courses and tutorials on Android app development!

  • Android SDK
    Android O: How to Use Notification Channels
    Chike Mgbemena
  • Android
    How to Solve Android’s 13 Most Common Error Messages
    Jessica Thornsby
  • Android SDK
    Create an Intelligent App With Google Cloud Speech and Natural Language APIs
    Ashraff Hathibelagal

12:00

Introducing NumPy

Numeric is a package that was originally developed by Jim Hugunin. It is considered the ancestor of NumPy, a Python library and an open-source project created by Travis Oliphant which stands for Numerical Python. Travis created NumPy by incorporating features of the Numarray package into Numeric. 

The fundamental idea of NumPy is support for multidimensional arrays. So NumPy can be considered as the base for numerical computing in Python, and has been created to enable Python to be used in solving mathematical and scientific problems. The NumPy module provides us with hundreds of useful mathematical functions in addition to constants such as the base of natural logarithms (e) and pi (π).

This tutorial shows how we can use NumPy to work with multidimensional arrays, and describes the ndarray object, a fundamental object of the library.

Installing NumPy

Since Python doesn't come bundled with NumPy, the first step to use this library is to go ahead and install it. This can be simply done by running the following command in your command prompt:

To make sure that NumPy was installed successfully, run the following commands in Python's IDLE:

Python-IDLE-Numpy

If the import statement at least runs successfully, then you are all set!

The ndarry Object

The ndarray is a fundamental object of NumPy. This object is an N-dimensional array, meaning that it contains a collection of elements of the same type indexed using N (dimensions of the array) integers.

The main attributes of ndarray are data type (dtype), shape, size, itemsize, data, and ndim. Let's learn what each attribute means through an example. 

In this example we are going to use NumPy to create an array. I will not give the dimensions of the array and other information, as we will see that using the above attributes.

create_array_numpy

Notice that we used the array function to create an array. The output of the above script is as follows:

array_numpy_output

Let's now return to our attributes.

dtype

The dtype attribute can be run as shown in the following statement:

The above statement will return int32 as the data type. This means that the elements of the array are of type int32. I'm getting 32 as I'm using a 32-bit Python. If you are using a 64-bit Python, you will get int64, but we are dealing with integers at the end.

Since NumPy is used in scientific computing, it has many data types, as shown in the documentation. Notice that the majority of the NumPy data types end with a number, which indicates the number of bits associated with that type (this was mentioned briefly in the above paragraph).

The following examples show how we can convert from one type to another:

The above statements return the following:

Although we can convert from one type to another, it is important to note that we cannot convert a complex number into an integer or a float.

shape

The shape attribute returns a tuple of the array dimensions. So the following statement:

will return (4,4), meaning that our array is composed of 4 rows and 4 columns.

size

The size attribute returns the number of elements in the array. Thus, if we type:

we will get 16 as the result, meaning that we have 16 elements in our array.

itemsize

The itemsize attribute returns the size of one array element in bytes. The following statement:

will return 4. This means that each array element is of size 4-bytes.

data

The data attribute is a Python buffer object that points to the start of the array's data. If we type the following:

we will get the following: <memory at 0x0000021E7E8D7EA0>.

ndim

The attribute ndim will return the number of the array dimensions. So typing the following statement:

will return 2, that is the array consists of two dimensions.

After understanding what the different ndarray attributes mean, let's take a look at some more examples of using ndarray.

Example 1

Say we want to create a new array with one row and five columns. We would do that as follows:

The output of the above statement is: [1 2 3 4 5].

Example 2

In this example, I'm going to rewrite the first example in this tutorial, but using [ ] instead of ( ), as follows:

numpy_array_square_brackets

Example 3

This example shows how we use a structured data type, where we declare the field name and the corresponding data type:

If we print(data_type), we will get the following:

We can apply the height_type to an ndarray object, as follows:

Selecting Items

In this section I'm going to show you how to select specific items in the array. For our array shown above under the "ndarry Object" section, let's say we want to select the item located on the third row and the fourth column. We will do that as follows:

Remember that indexing here starts at 0, and that's why we wrote [2,3] instead of [3,4].

More on NumPy Arrays

In this section, we are going to delve deeper into NumPy arrays.

Empty (Uninitialized) Arrays

We can create an empty array using numpy.empty with the following syntax:

The meanings of the parameters in the above constructor are as follows

  • Shape: the shape (dimensions) of the empty array.
  • dtype: the desired output type, which is optional.
  • Order: if you want a C-style (row-major) array, you would type C; if you want a FORTRAN-style (column-major) array, you would type F.

So let's create an empty [2,2] array of type int. We can do that as follows:

The above script will return the following random values as the array wasn't initialized:

Array Filled With Zeros

In order to create an array where the elements are all zeros, we use numpy.zeros. The constructor here has the same syntax and parameters as in numpy.empty. So, if we want to create a [2,2] zeros array of type int, we can do that as follows:

The above script will return the following:

An array with all elements having the value 1 can be simply created in the same way as above, but with numpy.ones.

Arrays With Evenly Spaced Values Within a Given Range

We can use numpy.arange to create an array with evenly spaced values within a specified range. The constructor has the following format:

Below is the meaning of each parameter:

  • Start: this is where the interval begins. The default value is 0.
  • Stop: the end of the interval, provided that this number is not included.
  • Step: the spacing between values. The default value is 1.
  • dtype: the data type of the output. If not specified, the data type will be the same as that of the input.

Let's take an example of numpy.arange.

The result of the above script is:

Reshaping an Array

In order to reshape an array, we use the numpy.reshape function. This function gives a new shape to an array without changing its data. As shown in the documentation, the function has the following attributes: numpy.reshape(a, newshape, order='C'), where a is the array we would like to reshape, newshape is the new desired shape provided that the new shape should be compatible with the origin shape, and order is an optional argument which refers to the index order we would like to use to both read the array a and how we would like to place the elements in the reshaped array.

C means reading/writing the elements using C-like index order; F means reading/writing the elements using Fortran-like index order, and A means reading/writing the elements in Fortran-like index order if a is Fortran contiguous in memory, C-like order otherwise.

I know I've covered a lot in the above paragraph, but the concept is very simple. Let's take our original array my_array and try to reshape it. Remember that the new array (reshaped array) has to be compatible with the original array. For instance, my_array has the shape (4,4), that is we have 16 elements in the array, and the new array has to have that number of elements.

We can reshape my_array by setting it to have eight rows and two columns, as follows:

In which case we would have the following output, where we also have 16 elements.

What if we write the reshape statement as follows?

In this case, you would get the following error:

Concatenating Arrays

If we want to join two or more arrays of the same shape along a specific axis, we can use the numpy.concatenate function. The syntax of this function is: numnumpy.concatenate((a1, a2, ...), axis=0)y.concatenate. a1 and a2 are arrays having the same shape, and axis is the axis along which the arrays will be joined, provided that the default is 0.

Again, let's take an example to simplify the concept. In this example, we will be joining (concatenating) three arrays.

The output of the above code is as follows:

Splitting Arrays

Contrary to joining arrays as shown in the above section, let's see how we can split (divide) an array into multiple sub-arrays. This can be done using the following function:

ary is the array to be divided into sub-arrays. Regarding indices_or_sections, if it is an integer N, the array will be divided into N equal arrays along the axis. If it is a 1-D array of sorted integers, the entries indicate where along the axis the array is split. axis is the axis along which to split.

The following example will reverse what we have done in the previous example, that is to return the concatenated array into its three array constituents:

The output of the above script is:

Conclusion

As we saw in this tutorial, NumPy makes it very flexible to work with arrays. The tutorial was just a scratch on the surface of this interesting Python library. NumPy still has many more features to look at to get the most out of this library. A comprehensive book on the topic by the NumPy creator himself is Guide to NumPy.

Furthermore, see what we have available for sale and for study in the marketplace, and don't hesitate to ask any questions and provide your valuable feedback using the feed below.

04:47
Flatt Bonnie x Giant RobotSDCC Exclusive Baby Unicorn as WonderCorn
04:11
Paul Kaiju for Summer Slime @ Gunnzo (07.22)
03:17
De Korner at SDCC: Exclusive Boxing Brain + New Pins

July 13 2017

14:13

SpriteKit Basics: Putting It All Together

Final product image What You'll Be Creating

In this post we'll build a simple game from scratch. Along the way, we'll touch on some of the most important aspects of the SpriteKit library.

This post builds on what we've learned earlier in the SpriteKit Basics series. If you want to refresh your SpriteKit knowledge, take a look at some of my other posts.

  • SpriteKit
    Introducing SpriteKit
    James Tyner
  • SpriteKit
    SpriteKit Basics: Nodes
    James Tyner
  • iOS SDK
    SpriteKit Basics: Sprites
    James Tyner
  • SpriteKit
    SpriteKit Basics: Actions and Physics
    James Tyner

New Project

Open Xcode and start a new project from the menu File > New Project. Make sure iOS is selected and choose Game as your template.

new project

Give your project a name, and make sure that Language is set to Swift, Game Technology is set to SpriteKit, and Devices is set to iPad.

project options

Planning the Game Scenes

One of the first things I like to do when creating a project is to determine how many scenes I will need for the project. I will usually have at least three scenes: an intro scene, a main game scene, and a scene to show high scores, etc.

For this example, we just need an intro and main gameplay scene since we won't be keeping track of lives, scores, etc. SpriteKit already comes with one scene when you create a new project, so we just need an intro scene.

From Xcode's menu, choose File > New > File. Make sure iOS is selected, and choose Cocoa Touch Class.

new cocoa touch class

Name the class StartGameScene, and make sure that Subclass of is set to SKScene and Language is set to Swift.

startgamescene class

Setting Up GameViewController

Open GameViewController.swift. Delete everything in that file and replace it with the following.

When you create a new project, GameViewController.swift is set up to load GameScene.sks from disk. GameScene.sks is used along with SpriteKit's built-in scene editor, which allows you to visually lay out your projects. We will not be using GameScene.sks, and will instead create everything from code, so here we initiate a new instance of StartGameScene and present it.

Create the Intro Scene

Add the following to the newly created StartGameScene.swift.

This scene is pretty simple. In the didMove method, we add a logo and a button. Then, in touchesBegan, we detect touches on the new game button and respond by loading the main scene GameScene.

Planning Game Classes

The next thing I like to do when creating a new game is decide which classes I will need. I can tell right away that I will need a Player class and an Enemy class. Both of these classes will extend SKSpriteNode. I think for this project we will just create the player and enemy bullets right from within their respective classes. You could make separate player bullet and enemy bullet classes if you prefer, and I suggest you try to do that as an exercise on your own. 

Lastly, there are the islands. These do not have any specific functionality but to move down the screen. In this case, since they're just decorations, I think it's also okay not to create a class, and instead just create them in the main GameScene.

Creating the Player Class

From Xcode's menu, choose File > New > File.  Make sure iOS is selected and choose Cocoa Touch Class.

new cocoa touch class

Make sure that Class is set to Player, Subclass of: is set to SKSpriteNode, and Language is set to Swift.

player class

Now add the following to Player.swift.

Within the init() method, we set up the physicsBody and invoke generateBullets(). The generateBullets method repeatedly calls fireBullet(), which creates a bullet, sets its physicsBody, and moves it down the screen.

When the player loses a life, the respawn() method is invoked. Within the respawn method, we fade the plane in and out five times, during which time the player will be invincible. One the player has exhausted all the lives, the kill() method is invoked. The kill method simply loads the StartGameScene.

Creating the Enemy Class

Choose File > New > File from Xcode's menu. Make sure iOS is selected and choose Cocoa Touch Class.

new cocoa touch class

Make sure that Class is set to EnemySubclass of: is set to SKSpriteNode, and Language is set to Swift.

Add the following to Enemy.swift.

This class is pretty similar to the Player class. We set its physicsBody and invoke generateBullets(). The move() simply moves the enemy down the screen.

Creating the Main Game Scene

Delete everything within GameScene.swift and add the following.

We create an instance of Player and an instance of CMMotionManager. We are using the accelerometer to move the player in this game.

Within the didMove(to:) method we turn off the gravity, set up the contactDelegate, add an edge loop, and set the player's position before adding it to the scene. We then invoke setupAccelerometer(), which sets up the accelerometer, and invoke the addEnemies() and generateIslands() methods.

The addEnemies() method repeatedly calls the generateEnemy() method, which will create an instance of Enemy and add it to the scene.

The generateIslands() method works similarly to the addEnemies() method in that it repeatedly calls createIsland() which creates an SKSpriteNode and adds it to the scene. Within createIsland(), we also create an SKAction that moves the island down the scene.

Within the didBegin(_:) method, we check to see which nodes are making contact and respond by removing the appropriate node from the scene and invoking player.die() if necessary. The createExplosion() method creates an explosion animation and adds it to the scene. Once the explosion is finished, it is removed from the scene.

Conclusion

During this series, we learned some of the most important concepts used in almost all SpriteKit games. We ended the series by showing how simple it is to get a basic game up and running. There are still some improvements that could be made, like a HUB, high scores, and sounds (I included a couple of MP3s you can use for this in the repo). I hope you learned something useful throughout this series, and thanks for reading!

If you want to learn more about game programming with SpriteKit, check out one of our comprehensive video courses! You'll learn how to build a SpriteKit game from A to Z.

  • Swift
    Code a Side-Scrolling Game With Swift 3 and SpriteKit
    Derek Jensen
  • Game Development
    Game Development With Swift and SpriteKit
    Derek Jensen

08:00

Grab 16 Free Stock Photos and Design Assets From Envato Elements

Last month, we made a big announcement: unlimited stock photos on Envato Elements. Now, to celebrate the launch, we're giving away 16 free images and design assets, to give a small taste of what Elements has to offer. They all come with a simple license allowing you to use them in any single project you want.

The Freebies You Can Download

So what exactly can you download? You'll find a selection of beautiful photos, such as this stunning underwater shot:

Turtle swimming underwater

You'll also find striking portraits, creative close-up shots, landscape images, and work photos suitable for illustrating a report or proposal:

Colleagues working

And it's not just images you can download. There's also an amazing Photoshop action that allows you simply to brush onto areas of a photo and play the action to turn those areas into a powerful sandstorm effect.

Sandstorm Photoshop action

And on top of all this, you can download an elegant script font, a flyer template, and a PowerPoint presentation template. 

How to Unlock Your Free Photos

To access the free photos and design assets, all you have to do is go to the launch page and enter your email address. Then you can download as many of the free files as you want. 

If you like what you see, you can also explore the full range of more than 200,000 stock photos now available on Envato Elements, or browse the thousands of web templates, 3D renders, graphics and more that come with a monthly subscription. But if it's not right for you, no worries—you can just enjoy your freebies, with no obligation to buy anything. 

00:38
Whats New EP5: Sket One

July 12 2017

18:03
UNKLE x Super 7 25th Anniversary Action Figures (7.22-7.23)
17:46

Unit Testing in React: Shallow vs. Static Testing

 

In my course on Unit Testing React Components, I showed you how to use Enzyme, the Airbnb unit-testing library, to unit test React components.

Enzyme gives us a couple of different types of ways to render React components. In this video from the course, we’ll look at the difference between shallow rendering and static rendering, and talk about when you might use them.

Related Links

Unit testing in React

Watch the Full Course

The full course, Unit Testing React Components, goes into much more detail on unit testing React components, which is actually easy to do thanks to React's functional programming model. We'll go from the basic setup and writing a test all the way through to full DOM rendering tests.

If you want to see what else you can do with JavaScript, check out the wide range of JavaScript plugins on Envato Market.

16:08

Melhor assim!

<!--[if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:AllowPNG/> </o:OfficeDocumentSettings></xml><![endif]-->
<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves/> <w:TrackFormatting/> <w:HyphenationZone>21</w:HyphenationZone> <w:PunctuationKerning/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>PT</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:SplitPgBreakAndParaMark/> <w:EnableOpenTypeKerning/> <w:DontFlipMirrorIndents/> <w:OverrideTableStyleHps/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="--"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument></xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false" DefSemiHidden="false" DefQFormat="false" DefPriority="99" LatentStyleCount="375"> <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 2"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 3"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 4"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 5"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 6"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 7"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 8"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 9"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 1"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 2"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 3"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 4"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 5"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 6"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 7"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 8"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="header"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footer"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index heading"/> <w:LsdException Locked="false" Priority="35" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="caption"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of figures"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope return"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="line number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="page number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of authorities"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="macro"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="toa heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 5"/> <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Closing"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Signature"/> <w:LsdException Locked="false" Priority="1" SemiHidden="true" UnhideWhenUsed="true" Name="Default Paragraph Font"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Message Header"/> <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Salutation"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Date"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Block Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Hyperlink"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="FollowedHyperlink"/> <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/> <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Document Map"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Plain Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="E-mail Signature"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Top of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Bottom of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal (Web)"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Acronym"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Cite"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Code"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Definition"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Keyboard"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Preformatted"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Sample"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Typewriter"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Variable"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Table"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation subject"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="No List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Contemporary"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Elegant"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Professional"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Balloon Text"/> <w:LsdException Locked="false" Priority="39" Name="Table Grid"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Theme"/> <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/> <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading"/> <w:LsdException Locked="false" Priority="61" Name="Light List"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/> <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/> <w:LsdException Locked="false" Priority="34" QFormat="true" Name="List Paragraph"/> <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/> <w:LsdException Locked="false" Priority="30" QFormat="true" Name="Intense Quote"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/> <w:LsdException Locked="false" Priority="19" QFormat="true" Name="Subtle Emphasis"/> <w:LsdException Locked="false" Priority="21" QFormat="true" Name="Intense Emphasis"/> <w:LsdException Locked="false" Priority="31" QFormat="true" Name="Subtle Reference"/> <w:LsdException Locked="false" Priority="32" QFormat="true" Name="Intense Reference"/> <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/> <w:LsdException Locked="false" Priority="37" SemiHidden="true" UnhideWhenUsed="true" Name="Bibliography"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/> <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/> <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/> <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/> <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/> <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/> <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Mention"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Smart Hyperlink"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Hashtag"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Unresolved Mention"/> </w:LatentStyles></xml><![endif]--><!--[if gte mso 10]><style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:"Tabela normal"; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin-top:0cm; mso-para-margin-right:0cm; mso-para-margin-bottom:8.0pt; mso-para-margin-left:0cm; line-height:107%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri",sans-serif; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-fareast-language:EN-US;} </style><![endif]-->
     

Os meus pensamentos vão se afastando de mim. Vejo-os ao longe, sem os distinguir, matéria compacta da qual se evadiram formas, texturas e contornos.   Melhor assim! Passar pelas coisas sem as ver, sem as sentir, como quando, às escuras, contornamos a cómoda do quarto sem a vermos, sem a pensarmos porque a sabemos ali e sabemos o espaço que ocupa, um saber intuitivo que já não necessita do sujeito para ser. Um saber que, de tão objectivamente assimilado, se esgota, subjectivo e incorpóreo. A possibilidade de tornar mais ampla a compreensão das coisas, engolida, sofregamente, pela invisibilidade a que são votadas. Conscientemente. Melhor assim!

E, contudo, no lugar em branco deixado pelos pensamentos, minúsculas borboletas em alvoroço buscam uma flor para morar. 



(imagem: pesquisa Google s/ ind. autoria) 


 
12:00

Working With the File System in Elixir

Working with the file system in Elixir does not really differ from doing so using other popular programming languages. There are three modules to solve this task: IO, File, and Path. They provide functions to open, create, modify, read and destroy files, expand paths, etc. There are, however, some interesting gotchas that you should be aware of.

In this article we will talk about working with the file system in Elixir while taking a look at some code examples.

The Path Module

The Path module, as the name suggests, is used to work with file system paths. The functions of this module always return UTF-8 encoded strings.

For instance, you can expand a path and then generate an absolute path easily:

Note, by the way, that in Windows, backslashes are replaced with forward slashes automatically. The resulting path can be passed to the functions of the File module, for example:

Here we are constructing a full path to the file and then writing some contents to it.

All in all, working with the Path module is simple, and most of its functions do not interact with the file system. We will see some use cases for this module later in the article.

IO and File Modules

IO, as the name implies, is the module to work with input and output. For example, it provides such functions as puts and inspect. IO has a concept of devices, which can be either process identifiers (PID) or atoms. For instance, there are :stdio and :stderr generic devices (which are actually shortcuts). Devices in Elixir maintain their position, so subsequent read or write operations start from the place where the device was previously accessed.

The File module, in turn, allows us to access files as IO devices. Files are opened in binary mode by default; however, you might pass :utf8 as an option. Also when a filename is specified as a character list ('some_name.txt'), it is always treated as UTF-8.

Now let's see some examples of using the modules mentioned above.

Opening and Reading Files With IO

The most common task is, of course, opening and reading files. To open a file, a function called open/2 can be used. It accepts a path to the file and an optional list of modes. For example, let's try to open a file for reading and writing:

You may then read this file using the read/2 function from the IO module as well:

Here we are reading the file line by line. Note the :eof atom that means "end of file".

You can also pass :all instead of :line to read the whole file at once:

In this case, :eof won't be returned—instead, we get an empty string. Why? Well, because, as we said earlier, devices maintain their position, and we start reading from the previously accessed place.

There is also an open/3 function, which accepts a function as the third argument. After the passed function has finished its work, the file is closed automatically:

Reading Files With File Module

In the previous section I've shown how to use IO.read in order to read files, but it appears that the File module actually has a function with the same name:

This function returns a tuple containing the result of the operation and a binary data object. In this example it contains "test", which is the contents of the file.

If the operation was unsuccessful, then the tuple will contain an :error atom and the error's reason:

Here, :enoent means that the file does not exist. There are some other reasons like :eacces (has no permissions).

The returned tuple can be used in pattern matching to handle different outcomes:

In this example, we either print out the file's contents or display an error reason.

Another function to read files is called read!/1. If you have come from the Ruby world, you've probably guessed what it does. Basically, this function opens a file and returns its contents in the form of a string (not tuple!):

However, if something goes wrong and the file cannot be read, an error is raised instead:

So, to be on the safe side, you can, for example, employ the exists?/1 function to check whether a file actually exists: 

Great, now we know how to read files. However, there is much more we can do, so let's proceed to the next section!

Writing to Files

To write something to a file, use the write/3 function. It accepts a path to a file, the contents, and an optional list of modes. If the file does not exist, it will be created automatically. If, however, it does exist, all its contents will be overwritten by default. To prevent this from happening, set the :append mode:

In this case, the contents will be appended to the file and :ok will be returned as a result. If something goes wrong, you'll get a tuple {:error, reason}, just like with the read function.

Also, there is a write! function that does pretty much the same, but raises an exception if the contents cannot be written. For example, we can write an Elixir program that creates a Ruby program that, in turn, prints "hello!":

Streaming Files

The files can indeed be pretty large, and when using the read function you load all the contents into the memory. The good news is that files can be streamed quite easily:

In this example, we open a file, stream it line by line, and inspect each line. The result will look like this:

Note that the new line symbols are not removed automatically, so you may want to get rid of them using the String.replace/4 function.

It is a bit tedious to stream a file line by line as shown in the previous example. Instead, you can rely on the stream!/3 function, which accepts a path to the file and two optional arguments: a list of modes and a value explaining how a file should be read (the default value is :line):

In this piece of code we are streaming a file while removing newline characters and then printing out each line. File.stream! is slower than File.read, but we don't need to wait until all lines are available—we can start processing the contents right away. This is especially useful when you need to read a file from a remote location.

Let's take a look at a slightly more complex example. I'd like to stream a file with my Elixir script, remove newline characters, and display each line with a line number next to it:

Stream.with_index/2 accepts an enumerable and returns a collection of tuples, where each tuple contains a value and its index. Next, we just iterate over this collection and print out the line number and the line itself. As a result, you'll see the same code with line numbers:

Moving and Removing Files

Now let's also briefly cover how to manipulate files—specifically, move and remove them. The functions we're interested in are rename/2 and rm/1. I won't bore you by describing all the arguments they accept as you can read the documentation yourself, and there is absolutely nothing complex about them. Instead, let's take a look at some examples.

First, I'd like to code a function that takes all files from the current directory based on a condition and then moves them to another directory. The function should be called like this:

So, here I want to grab all .txt files and move them to the texts directory. How can we solve this task? Well, firstly, let's define a module and a private function to prepare a destination directory:

mkdir!, as you've already guessed, tries to create a directory and returns an error if this operation fails.

Next, we need to grab all the files from the current directory. This can be done using the ls! function, which returns a list of file names:

Lastly, we need to filter the resulting list based on the provided function and rename each file, which effectively means moving it to another directory. Here is the final version of the program:

Now let's see the rm in action by coding a similar function that is going to remove all files based on a condition. The function will be called in the following way:

Here is the corresponding solution:

rm!/1 will raise an error if the file cannot be removed. As always, it has an rm/1 counterpart that will return a tuple with the error's reason if something goes wrong.

You may note that the remove_if and transfer_to functions are very similar. So why don't we remove code duplication as an exercise? I'll add yet another private function that takes all the files, filters them based on the provided condition, and then applies an operation to them:

Now simply utilize this function:

Third-Party Solutions

Elixir's community is growing, and fancy new libraries solving various tasks are emerging. The Awesome Elixir GitHub repo lists some popular solutions, and of course there is a section with libraries for working with files and directories. There are implementations for file uploading, monitoring, filename sanitization, and more.

For example, there is an interesting solution called Librex for converting your documents with the help of LibreOffice. To see it in action, you can create a new project:

Then add a new dependency to the mix.exs file:

After that, run:

Next, you can include the library and perform conversions:

In order for this to work, the LibreOffice executable (soffice.exe) must be present in the PATH. Otherwise, you'll need to provide a path to this file as a third argument:

Conclusion

That's all for today! In this article, we've seen the IO, File and Path modules in action and discussed some useful functions like open, read, write, and others. 

There are lots of other functions available for use, so be sure to browse Elixir's documentation. Also, there is an introductory tutorial on the official website of the language that can come in handy as well.

I hope you enjoyed this article and now feel a bit more confident about working with the file system in Elixir. Thank you for staying with me, and until next time!

12:00

How to Add CAPTCHAs to Android Apps

If you are developing an Android application that needs to interact with your back-end server, it is important that you take steps to defend it against bots—automated scripts that try to pass for human. Otherwise, your server stands a very real chance of being overrun by them.

CAPTCHAs, short for Completely Automated Public Turing tests for telling Computers and Humans Apart, are currently the most effective defense against bots. As you might already know, they are usually image, voice, or common sense-based tests that are easy for humans, but extremely hard for computers.

In this tutorial, I'll show you how to use the newly released SafetyNet reCAPTCHA API to add CAPTCHAs to your Android apps.

Prerequisites

To be able to follow this tutorial, you'll need the following:

1. Project Setup

Launch Android Studio and create a new project with an empty activity. In the Configure your new project form, make sure you type in a meaningful package name—you'll be using it while registering your app with the reCAPTCHA service.

Additionally, check the Include Kotlin Support field. Because Kotlin is now an official Android language, we'll be using it instead of Java in this tutorial.

Configure your new project form

SafetyNet APIs are a part of Google Play services. To be able to use them in your project, add the following implementation dependency to the build.gradle file of the app module:

Additionally, to perform network-related operations, we'll be using the Fuel library, which has a very concise Kotlin-based API. Therefore, add it as another implementation dependency.

You can't perform network-related operations without the INTERNET permission, so add the following line to your project's manifest file:

Finally, press the Sync Now button to complete the project configuration.

2. Acquiring reCAPTCHA Keys

You'll need two keys before you can use the reCAPTCHA service:

  • a site key, which must be passed to the service from your Android app
  • and a secret key, which must be passed to the service from your back-end server

To get the keys, use your Google account and log in to the reCAPTCHA admin console. If you're opening the console for the first time, you'll automatically be presented with a short registration form where you can type in your app's package name.

reCAPTCHA registration form

After you accept the reCAPTCHA terms of service, go ahead and press the Register button to generate both the keys.

Site key and secret key screen

You can now add the site key to your Android Studio project by simply mentioning it inside the res/values/strings.xml file:

We'll be working with the secret key only towards the end of this tutorial, so note it down somewhere safe for now.

3. Generating CAPTCHAs

When we hear the word CAPTCHA, we usually think of messy images containing hard-to-read letters and numbers. Such CAPTCHAs, however, thanks to advances in computer vision technologies, are no longer good enough to stop all bots.

CAPTCHAs generated by the reCAPTCHA service are highly advanced, and very interactive too. In fact, solving them is akin to playing simple games. Consequently, you can't directly embed them in your activity's layout. Instead, you must add a button to the layout, which, when pressed, should lead the user to a new screen or dialog containing the CAPTCHAs.

The following code shows you how to add a Button widget to your activity's layout XML file:

Before you start generating CAPTCHAs, you'll have to initialize a client for the SafetyNet API. You can do so by calling the getClient() method of the SafetyNet class. Accordingly, add the following code inside the onCreate() method of your Activity class:

The CAPTCHAs must be displayed when the user presses the button, so add an on-click event handler to it using the setOnClickListener() method. Inside the handler, all you need to do is call the verifyWithRecaptcha() method and pass your site key as an argument to it in order to open a dialog containing a CAPTCHA.

The return value of the verifyWithRecaptcha() method is a Task object. By attaching an on-success event handler to it, you'll be able to acquire a RecaptchaTokenResponse object containing a token you can use to tell if the user passed or failed the CAPTCHA. Here's how:

4. Validating CAPTCHA Tokens

The token you got in the previous step must again be passed to the reCAPTCHA service to check if the user passed or failed the test. However, this time, the call to the reCAPTCHA service must be made from your back-end server.

The server, of course, won't have the token unless your Android app sends it to it. Therefore, we must now write code to send tokens from the Android app to the server.

For now, let's assume that our server has an endpoint called validate, which can accept the token as a query string parameter. I'll be using 10.0.2.2 as the server's IP address and 8000 as its port. If you intend to run the server on your own computer and the app on an emulator running on the same computer, you too can use the same IP address.

You can now call the httpGet() method offered by the Fuel library to send the token to the server. The method expects a list of query string parameters as its only argument, so I suggest you use the listOf() utility method to create a list containing a single item: the token assigned to a query parameter named user_token.

Because the httpGet() method runs asynchronously, you must call the responseString() method in order to handle its return value. The following code shows you how:

You can see that we now have access to a result object. In case of no errors, it will contain our server's response as a string.

Let's assume that our server returns the string "PASS" if the user passes the test, and "FAIL" otherwise. What you actually do when the user passes or fails the test is, of course, up to you. For now, I suggest you simply display appropriate Toast messages. The following code shows you how to do so concisely:

At this point, the app is ready. You can go ahead and deploy it to your emulator or device.

CAPTCHA dialog shown when user presses the button

5. Creating the Server

We made a lot of assumptions about our web server in earlier steps. It's now time to create it, making sure that it doesn't deviate from those assumptions.

A quick and easy way to create a fully functional web server is to use the Node.js platform and the Express.js framework. To create a new Node.js project, make a new directory on your computer and run the npm init command inside it.

To add the Express framework to the project, you can use the npm install command.

Additionally, we'll be needing the Request package to communicate with the reCAPTCHA service. Therefore, install it as another dependency.

You can now use your favorite code editor to create a new file called index.js and start writing all the required server-side code.

Start by loading both the express and request modules using the require() function and creating a new Express application by calling the express() function.

Our Express application must have an endpoint called validate, which can be accessed using the HTTP GET method. Therefore, create a new route for it using the get() method:

To validate the token generated by the Android app, you must now make a POST request to the reCAPTCHA service. The request must contain your secret key and the token itself. The following code shows you how to build the POST request's body by extracting the token from the query string:

To actually make the POST request, you can call the post() method of the request module. Its response is a short JSON document containing a key called success. As you might expect, its value is true only if the user has passed the test.

The following code shows you how to parse the JSON document, extract the success key, and generate the "PASS" and "FAIL" responses our Android app needs:

Lastly, you must call the listen() method of the Express application object to allow it to listen for connections.

At this point our web server is ready. To start it, return to the terminal and run the following command:

If you run your Android app now, press the button, and successfully solve a CAPTCHA, you should see a Toast message telling you that you are human.

Toast message shown when user passes the CAPTCHA

Conclusion

You now know how to use the SafetyNet reCAPTCHA API to secure your Android app and back-end infrastructure against bots. You don't have to worry about automated signups, screen scrapers, or bot-generated spam any more.

To learn more about the reCAPTCHA API, you can refer to the official documentation.

In the meantime, check out some of our other great posts on Android app development!

  • Android SDK
    How to Create an Android Chat App Using Firebase
    Ashraff Hathibelagal
  • Android Lollipop
    Getting Started With RecyclerView and CardView on Android
    Ashraff Hathibelagal
  • Android
    How to Get Started With Push Notifications On Android
    Ashraff Hathibelagal

06:22
Marsham Toy Hour S2 E14 with Teresa Chiba and Don Kratzer
05:33
Kozik x Blackbook Toy Lil Alex and Dim Stealth Edition
Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.

Don't be the product, buy the product!

Schweinderl