Mobile technology for the masses: Coding gestures in Android

Mobile technology for the masses: Coding gestures in Android

Convert buttons in Android mobile applications into swipe gestures

Mobile users are often distracted, busy, and ergonomically constrained, so you need to build your mobile app UI accordingly. Andrew Glover discusses the key factors that differentiate mobile apps from web apps, then walks you through creating a mobile app UI that uses swipe gestures instead of buttons for navigation.

When building a mobile app, just like building a web app, keep the end goal (and the user) in mind. Always understand what benefit your app will provide to the user. What type of information will your app display, what functionality will it have, and how will the user access it? Paying adequate attention to the user experience of your mobile app will help ensure success.

About this series: Mobile application releases have exploded in recent years, and so has the market for mobile development technologies. This is a new series that introduces mobile applications to developers who are familiar with programming but new to mobile technologies. It starts with coding native applications with Java code, and then expands the toolkit to include JVM languages, scripting frameworks, HTML5/CSS/JavaScript, third-party tools, and more. This series of articles will take you step by step to master these necessary technologies and actually handle all mobile development scenarios.

Unlike traditional GUI development for desktop or web applications, the rule for mobile applications is to have less than too much. When you decide to design an application interface, you probably want to keep it simple and easy. Most mobile devices are small (unless you are dealing with a Samsung Note 4, which is the largest mobile phone I have ever seen, also known as a tablet). Small size is an important reason for their popularity, because people can carry them with them and use them at any time. This leads to another important discovery about mobile applications, most users do not pay attention to the application when using mobile devices.

Some mobile apps are built specifically for tablets for business use only (like doctors accessing patient records). Most mobile apps require users to access them from a tiny device when, let’s be honest, they’re also doing other things. I might play a few games of Angry Birds while I wait to buy some groceries online. I might check my email after getting off a long flight. But if it takes only two clicks or a swipe to load Angry Birds or an email message, I’ll probably stick with the mobile app.

Another factor that really separates mobile apps from traditional web and desktop apps is sheer volume: for every web app, there are easily 100 mobile apps. Provide multiple valuable services with your app, and make sure they are easy to use and engaging. If you expect users to use your app through RTM (reference manual), you are creating a problem for the user, and it will eventually become a problem for you. It doesn't matter if the user is a doctor accessing a patient's records, or someone playing Cut-the-Rope in the break room. If your app only takes a few minutes to install, the user is likely to search the app store for an app that takes even less time.

(Hello) Overheard Word

If you have read the first article in this series, you will know how to build an Android development environment in Eclipse, and the previous article has configured the Android SDK for Android 4.2. You should have completed your first Android application, the classic Hello World. In this article, you will continue to design a more unique application.

My sample application, Overheard Word, is designed to make learning new vocabulary and using it in context more fun and easier, which are two of my biggest passions, by the way. In this application, users can take a quiz after learning a few words. The interface consists of a display and two buttons. The display is for words and their corresponding definitions, while the buttons are for user navigation.

Overheard Word is a fun and simple mobile app for people who love learning words (I mean them as vocabulary addicts and word connoisseurs). More importantly, the sample serves as an example of building a legitimate Android application that you can deploy to real Android devices.

Targeting applications

Before designing an app, I like to assess the target market. The app built in Part 1 targets Android 4.2, or API version 17. Take a look at the most popular Android versions released by Google. (See Figure 1):

Figure 1. Android distribution, sorted by version number

Tablets and phones : In Figure 1, tablet sales are likely driven by the increase in Android 15 users. Regardless of the vendor, most tablets currently run Android 4.x. Unless you are building an Android-only PC app, you should target your app to Android APIs 9 and 10, which are the mainstream markets for mobile devices today.

Figure 1 shows that Android 2.3.x (API versions 9 and 10) currently only accounts for about half of the Android device market! If half of all Android devices are running Android 2.3.x, wouldn't it make sense to develop for that market?

Starting a new project

At the end of the last article, I had already installed API 4.2. Now I want to install Android 2.3.3 (API 10). When you start the Android SDK Manager again, as shown in Figure 2 , you will see that there are some updates to install:

Figure 2. A local Android SDK installer with only one installed version

If you want to follow along with me, click the Android 2.3.3 (API 10) installation option, keeping the recommended updates specified by your administrator.

Figure 3. Installing Android 2.3.3 (API 10)

After you download the new Android API, you must create a compatible emulator or Android Virtual Device (AVD). In the Android SDK Manager, select the Tools menu and choose Manage AVDs. Be sure to create an AVD that targets API Level 10.

Figure 4. Creating an AVD

Next, create a New Android Application project and give it a name. Mine is called Overheard Word Application. I target the application to API 10 to make it accessible to the widest range of devices. Looking closely at the screenshot in Figure 5 , you’ll notice that I also chose to compile against API 10. You can compile against a later version of Android, but I like to program and compile against the same API. (If I compile against a more recently released API, I must be aware that I cannot use any of the features in my application, which could cause compatibility issues.)

Figure 5. Creating a new Android project in Eclipse

When you click Next, make sure you do not save your application as a Library Project. In the next article, you will continue to keep the same default settings for the Hello World application. You can make some small changes later (for example, change the application's icon file).

The last step is to name your first Activity. I named mine OverheardWord instead of Main, and I named my layout similarly. If you're also targeting API 10, you'll have a few Navigation Type options in the final dialog. Don't worry about it now; I'll show you how to create a navigation menu in a subsequent article. For this app, leave your Navigation Type option at none.

Figure 6. Creating a default Activity

Click Finish to create a project that will be very similar to the default project you created last time.

#p#

Building an Android UI

You've now built a simple UI. Remember? Android UIs are ultimately defined as an XML document, which you're probably familiar with. If you come from traditional Java™ GUI development, you probably remember layout tools, which Android uses as well. You use Android's layout tools to define (actually, suggest) how a device should lay out your application's visual components. Layout styles include:

  • Linear: where components are laid out in a parallel and vertical manner (similar to newspaper columns)
  • Relative: where components are spaced apart from each other (Widget 2 is to the right of Widget 1, etc.)
  • Table: where widgets are displayed in rows or columns

There are many layout tools available, and this is just the beginning!

You use a layout tool to define widgets, which are common components you'll find in all GUIs. The Android platform provides basic widgets such as buttons, text boxes, and drop-down lists, as well as more complex widgets such as image viewers and scroll wheels.

I think my Overheard Word UI needs to:

  • Three text boxes (one for each word, pronunciation, and definition)
  • Two buttons (one to select a new word, one to take the quiz)

Defining the layout

To put together an Android UI, the first step is to define its layout and the widgets you want to use in it. In Listing 1 , I first define a LinearLayout with three TextViews. Next, I create a child layout (another LinearLayout) to hold two Buttons.

Listing 1. Defining a LinearLayout in Android

  1. < LinearLayout   xmlns:android = "http://schemas.android.com/apk/res/android"  
  2. xmlns:tools = "http://schemas.android.com/tools"  
  3. android:id = "@+id/LinearLayout1"  
  4. android:layout_width = "match_parent"  
  5. android:layout_height = "match_parent"  
  6. android:orientation = "vertical"  
  7. android:paddingBottom = "@dimen/activity_vertical_margin"  
  8. android:paddingLeft = "@dimen/activity_horizontal_margin"  
  9. android:paddingRight = "@dimen/activity_horizontal_margin"  
  10. android:paddingTop = "@dimen/activity_vertical_margin"  
  11. tools:context = ".OverheardWord"   >  
  12.  
  13. < TextView  
  14. android:id = "@+id/word_study_word"  
  15. android:layout_width = "wrap_content"  
  16. android:layout_height = "wrap_content"  
  17. android:layout_gravity = "center"  
  18. android:layout_marginBottom = "10dp"  
  19. android:layout_marginTop = "60dp"  
  20. android:textColor = "@color/black"  
  21. android:textSize = "30sp"   />  
  22.  
  23. < TextView  
  24. android:id = "@+id/word_study_part_of_speech"  
  25. android:layout_width = "wrap_content"  
  26. android:layout_height = "wrap_content"  
  27. android:layout_gravity = "center"  
  28. android:layout_marginBottom = "10dp"  
  29. android:layout_marginLeft = "20dp"  
  30. android:layout_marginRight = "20dp"  
  31. android:textColor = "@color/black"  
  32. android:textSize = "18sp"   />  
  33.  
  34. < TextView  
  35. android:id = "@+id/word_study_definition"  
  36. android:layout_width = "wrap_content"  
  37. android:layout_height = "wrap_content"  
  38. android:layout_gravity = "center"  
  39. android:layout_marginLeft = "40dp"  
  40. android:layout_marginRight = "40dp"  
  41. android:textColor = "@color/black"  
  42. android:textSize = "18sp"   />  
  43.  
  44. < LinearLayout  
  45. android:id = "@+id/widget62"  
  46. android:layout_width = "wrap_content"  
  47. android:layout_height = "wrap_content"  
  48. android:layout_gravity = "center"  
  49. android:layout_marginLeft = "5dp"  
  50. android:layout_marginRight = "5dp"   >  
  51.  
  52. < Button  
  53. android:id = "@+id/next_word"  
  54. android:layout_width = "wrap_content"  
  55. android:layout_height = "wrap_content"  
  56. android:layout_marginTop = "20dp"  
  57. android:text = "@string/next_word"   />  
  58.  
  59. < Button  
  60. android:id = "@+id/take_quiz"  
  61. android:layout_width = "wrap_content"  
  62. android:layout_height = "wrap_content"  
  63. android:layout_marginLeft = "50dp"  
  64. android:layout_marginTop = "20dp"  
  65. android:text = "@string/take_quiz"   />  
  66. </LinearLayout>  
  67.  
  68. </LinearLayout>  

As you can see from the XML in Listing 1 , each widget has a number of attributes that ultimately affect its appearance. For example, each button has a text element whose string points to a resource element. The resource element is defined in a resource file called strings.xml, which is located in the res/values ​​directory. Listing 2 shows the strings.xml file.

Listing 2. Resource file that defines button colors and labels

  1. <? xml   version = "1.0"   encoding = "utf-8" ?>  
  2. < resources >  
  3.  
  4. < string   name = "app_name" > Overheard Word </ string >  
  5.  
  6. < color   name = "white" > #ffffff </ color >  
  7. < color   name = "black" > #000000 </ color >  
  8.  
  9. < string   name = "next_word" > Next word </ string >  
  10. < string   name = "take_quiz" > Take quiz </ string >  
  11.  
  12. </ resources >  

If you want to see what my UI looked like before, you can start an instance of the simulator and click Graphical Layout in Eclipse, as shown in Figure 7 .

Figure 7. Overheard Word running in Eclipse

Placeholder text

Previewing my UI, I was a little confused by the spacing between word attributes. This isn’t a big deal though: to get a more realistic representation of the text display, I added some sample text before writing the code.

First, add an android:text attribute to each widget, just like you did for the button label. Now, the placeholder text is added, as shown below:

Listing 3. Using android:text to create a placeholder

  1. < TextView  
  2. android:id = "@+id/word_study_part_of_speech"  
  3. android:layout_width = "wrap_content"  
  4. android:layout_height = "wrap_content"  
  5. android:layout_gravity = "center"  
  6. android:layout_marginBottom = "10dp"  
  7. android:layout_marginLeft = "20dp"  
  8. android:layout_marginRight = "20dp"  
  9. android:textColor = "@color/black"  
  10. android:textSize = "18sp"   
  11. android:text = "Part of Speech" />  

As you can see in Listing 3 , I added sample text ("Part of Speech") to word_study_part_of_speechTextView. I updated some of the style elements by increasing the text size, defining the text color, and centering the widget in the layout. The attributes in Listing 3 are fairly common for definitions in Android apps; you'll get to know and love them as you build more apps. I defined the sample text directly in my layout file, rather than in a resource file, because the text values ​​were temporary during my design process.

When I run my application from Eclipse, I wonder what the user will see when they launch the application:

Figure 8. Overheard Word running in AVD

There is a slight problem with some aspects of UI design right now. Can you guess what it is? Yep, those annoying buttons. Are they necessary? Do users normally click buttons on mobile apps? Users might swipe left or right to see new words, or swipe down to take a quiz.

Removing the button cleans up the UI and makes the Overheard Word app better suited to the expectations of most mobile users. I was able to implement the swipe behavior pretty easily, as you'll see shortly.

Coding Gestures in Android

I defined the initial interface for the Overheard Word application and added some behaviors. I reapplied my traditional Java GUI development to Android, using listeners for the first time in Android to respond to events. If I were to keep the two buttons, I would probably use OnClickListener to attach behaviors to the button click events, and then attach them to the setOnClickListener method of each button widget.

Anonymous classes : An anonymous class is a class that is defined inside a class, but has no name: You can define and instantiate an anonymous class in one expression. I find anonymous classes convenient and useful, and I write a lot of them, especially when writing user interface behaviors.

While I'm sure I can use an OnClickListener, I'll probably end up with multiple class files if there's more than one widget. I don't like clutter, so I've chosen an alternate implementation strategy using anonymous classes.

Although swipe gestures are a concept not found in traditional GUI programming, their implementation is similar to programming a button click. I start a touch listener (which is part of the underlying Android platform) and then provide responsive behavior. To detect finger swipes, I also calculate the difference between the start and end of a gesture, as well as the swipe position. (I find this useful to consider that in Cartesian coordinates, left-right swipes are on the X axis, while up-down swipes are on the Y axis.)

Gestures can occur anywhere in the interface, and if you attach this type of listener to any component of your app's interface, you can start with a layout (which can be represented as a View in Android) and include the simplest button widget. I use Android's SimpleOnGestureListener to create my initial gesture implementation, and then attach it to the View through the onTouchListener method. By attaching it to the view instance, I can detect finger swipes across the entire interface, rather than just testing inside its components (such as the small image browser).

While this is possible, I would have to write a class that detects finger activity on a mobile device.

#p#

SwipeDetector

Button clicks are relatively easy to detect. The underlying operating system usually extracts the coordinate information of the button boundaries, so you can skip the manual definition step. Writing your application to recognize and respond to a swipe gesture is a bit more complicated. Android leaves it up to the programmer to write, which is a slightly more complex task but also gives you a lot of flexibility. You can choose to detect a single swipe gesture like I did, or you can detect other finger movements, such as friction, which is sometimes used in games.

Think about mobility : Are you curious if mobile listeners are still defined by action clicks? Last time I checked, users typically press buttons on mobile devices, not click them like they do with a mouse on a desktop. If you're a mobile developer, you need to think about mobility, which means rethinking traditional web and desktop mechanisms like button clicks, start menus, and even GUI wizards.

Writing a gesture swipe detection program involves Cartesian mathematics. That is, if the starting coordinate minus the ending coordinate on the X axis is greater than a predefined distance, then you can assume that the user swiped their finger from the right side of the application interface to the left side. The same method is used on the Y axis to detect up and down swipes.

It's easy to abstract this kind of logic into a simple class that does the math. SimpleOnGestureListener has a method called onFling. The onFling method takes two parameters of type MotionEvent (one for start and one for end) to represent the velocity on the X axis, and two float parameters to represent the velocity on the Y axis. Thus, the SwipeDetector class in Listing 4 has four methods that describe the full range of motion for a swipe: up, down, left, and right.

Listing 4. SwipeDetector

  1. import android.view.MotionEvent;
  2.  
  3. public   class SwipeDetector {
  4.  
  5. private   int swipe_distance;
  6. private   int swipe_velocity;
  7. private   static   final   int SWIPE_MIN_DISTANCE = 120 ;
  8. private   static   final   int SWIPE_THRESHOLD_VELOCITY = 200 ;
  9.  
  10. public SwipeDetector( int distance, int velocity) {
  11. super ();
  12. this .swipe_distance = distance;
  13. this .swipe_velocity = velocity;
  14. }
  15.  
  16. public SwipeDetector() {
  17. super ();
  18. this .swipe_distance = SWIPE_MIN_DISTANCE;
  19. this .swipe_velocity = SWIPE_THRESHOLD_VELOCITY;
  20. }
  21.  
  22. public   boolean isSwipeDown(MotionEvent e1, MotionEvent e2, float velocityY) {
  23. return isSwipe(e2.getY(), e1.getY(), velocityY);
  24. }
  25.  
  26. public   boolean isSwipeUp(MotionEvent e1, MotionEvent e2, float velocityY) {
  27. return isSwipe(e1.getY(), e2.getY(), velocityY);
  28. }
  29.  
  30. public   boolean isSwipeLeft(MotionEvent e1, MotionEvent e2, float velocityX) {
  31. return isSwipe(e1.getX(), e2.getX(), velocityX);
  32. }
  33.  
  34. public   boolean isSwipeRight(MotionEvent e1, MotionEvent e2, float velocityX) {
  35. return isSwipe(e2.getX(), e1.getX(), velocityX);
  36. }
  37.  
  38. private   boolean isSwipeDistance( float coordinateA, float coordinateB) {
  39. return (coordinateA - coordinateB) > this .swipe_distance;
  40. }
  41.  
  42. private   boolean isSwipeSpeed( float velocity) {
  43. return Math.abs(velocity) > this .swipe_velocity;
  44. }
  45.  
  46. private   boolean isSwipe( float coordinateA, float coordinateB, float velocity) {
  47. return isSwipeDistance(coordinateA, coordinateB)
  48. && isSwipeSpeed(velocity);
  49. }
  50. }

Now that I have a handy class that tells me if my finger has swiped, I can embed it into my UI. Recall the default activity I first created, which I named OverheardWord. I kept all of the code that Eclipse provided, but added a few new things to listen for swipes and respond. I defined an Android GestureDetector that will perform an OnGestureListener implementation. I'm really grateful that the Android team created a handy class that I can use to do this with an anonymous class called SimpleOnGestureListener, and then override the onFling method.

Swipe detection in UI

In the onCreate method of the Activity, a GestureDetector is initially created using a SimpleOnGestureListener, which requires the use of my SwipeDetector class.

The next step is to create a small dialog box and specify the trigger event when the finger slides. This kind of small, temporary dialog box is called Toast here. Sometimes Toast has only one line of code.

Listing 5. GestureDetector

  1. private GestureDetector initGestureDetector() {
  2. return   new GestureDetector( new SimpleOnGestureListener() {
  3.          
  4. private SwipeDetector detector = new SwipeDetector();
  5.          
  6. public   boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  7. float velocityY) {
  8. try {
  9. if (detector.isSwipeDown(e1, e2, velocityY)) {
  10. return   false ;
  11. } else   if (detector.isSwipeUp(e1, e2, velocityY)) {
  12. showToast( "Up Swipe" );
  13. } else   if (detector.isSwipeLeft(e1, e2, velocityX)) {
  14. showToast( "Left Swipe" );
  15. } else   if (detector.isSwipeRight(e1, e2, velocityX)) {
  16. showToast( "Right Swipe" );
  17. }
  18. } catch (Exception e) {} //for now, ignore  
  19. return   false ;
  20. }
  21.          
  22. private   void showToast(String phrase){
  23. Toast.makeText(getApplicationContext(), phrase, Toast.LENGTH_SHORT).show();
  24. }
  25. });
  26. }

Now we'll create an initGestureDetector method that calculates the extent and speed of the user's finger swipe. First, create an anonymous SimpleOnGestureListener instance and provide an implementation of the onFling method. Notice how the onFling method uses the SwipeDetector. For example, if a matching item is swiped up, a Toast is displayed to indicate the movement.

The next step is to insert this swipe detection behavior into my UI. I start by registering a few listeners with the View instance.

Listing 6. Registering swipe detection on a view.

  1. private GestureDetector gestureDetector;
  2.  
  3. protected   void onCreate(Bundle savedInstanceState) {
  4. super .onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_overheard_word);
  6.  
  7. gestureDetector = initGestureDetector();
  8.  
  9. View view = findViewById(R.id.LinearLayout1);
  10.  
  11. view.setOnTouchListener( new View.OnTouchListener() {
  12. public   boolean onTouch(View v, MotionEvent event) {
  13. return gestureDetector.onTouchEvent(event);
  14. }
  15. });
  16.  
  17. view.setOnClickListener( new OnClickListener() {
  18. public   void onClick(View arg0) {
  19. }
  20. });
  21. }

I get a handle to the View instance by its Id in the onCreate method. Most Java developers prefer this handy Android-ism.

Find a view by ID

Remember when I defined the XML for the UI, the corresponding values ​​in various resource files (such as strings.xml)? When I compile the Android application, the XML will be turned into code, providing the corresponding Ids in class R. Before going further, you should examine the R class in the gen directory of your project.

Look at your layout XML file (you may recall that I named mine activity_overheard_word.xml). Each widget in the XML file has an id attribute. For example, for simplicity, the layout id of my application is android:id="@+id/LinearLayout1" or LinearLayout1. The id name is automatically generated by Eclipse and can be changed if necessary. The important thing is that this idLinearLayout1 has a corresponding attribute in the R class, as shown in Listing 7 .

Listing 7. Widget IDs in the R class

  1. public   static   final   class id {
  2. public   static   final   int LinearLayout1= 0x7f090000 ;
  3. public   static   final   int action_settings= 0x7f090004 ;
  4. public   static   final   int word_study_definition= 0x7f090003 ;
  5. public   static   final   int word_study_part_of_speech= 0x7f090002 ;
  6. public   static   final   int word_study_word= 0x7f090001 ;
  7. }

By combining the contents of the XML file with its corresponding R file, I can reference these widgets without having to parse the XML. Therefore, the onCreate method of the Activity is returned and I can apply the View widget by its id (R.id.LinearLayout1). The findViewById method is provided by the Android platform when extending an Activity.

Just swipe your finger and it's done easily!

Once I have the View instance, I can attach my gestureDetector instance via setOnTouchListener. In Listing 6 , another anonymous class is now handling all of my touch behavior. When the user touches the device screen, events are triggered and actions are taken according to the gestureDetector. I also implemented an OnClickListener and set it in the OnClickListener method; however, the anonymous class has no behavior in this case.

With this, I have a relatively simple interface that responds to a finger swipe. In this example, three of the swipe gestures are used: left, right, and up. Try this code in your AVD. To simulate a finger swipe, use the mouse to click and then drag up, down, or right.

Now, what will it be like to run this application under development on a real device?

Deploy a development

In response to the proliferation of viruses, Trojans, and malware that plague unsuspecting users, Apple and Google each introduced code signing for third-party apps that run on their devices. In theory, code signing ensures that reputable people build apps, ensuring that no one can tamper with them before you install them. In practice, Google and Apple sign code with the same degree of security. For example, when I sign my Android app, I can state that I am a national bank in good standing. I can't do that when I sign my iOS or Windows Phone 8 app.

In development mode, you don't have to worry about code signing. In fact, Android and Eclipse build binaries signed with a developer key, unknown to you. So, it's not difficult to get an app onto a device in development mode, and the easiest way is to plug your Andriod device into a computer USB port.

Next, go into Eclipse, right-click your project and select the Run As menu option, then click Run Configurations. Eclipse displays a configuration dialog box where you can select your project and then click the Target tab, as shown in Figure 9 .

Figure 9. Select a device to run your application.

You can choose to Always prompt to pick device (if you want to be selective) or Launch on all compatible devices/AVD's. If you choose the latter, you can select the Active Devices tab like I did and click Run. Assuming you are coding along with me, a few moments later, a beautiful application will appear on your device. Swipe your finger left, right, and up, and watch the magic happen, this simple dialog box will take care of everything.

Security Settings : If you can't load your app from Email or Dropbox, go to your device settings and enable Unknown Sources under Security Settings. You can use this feature to install apps, including apps you're developing, instead of installing them from Google Play.

Another way to deploy an application is to have its .apk file emailed to you, then open the file on your Android device and follow the automatic installation dialogs. You can also upload the .apk file to a service like Dropbox, then open Dropbox on your device to install it. The application's .apk file is in your project's bin directory, where Eclipse also saves the corresponding binary file.

Keep in mind that these deployment mechanisms are for development testing, not for publishing your application. Publishing your application and making money from it involves more steps, which I will cover in a subsequent article.

Conclusion

Designing a mobile app means thinking about everything a little simpler and easier. In this installment, you learned how to write a mobile app that responds to swipe gestures, without the button clicks that desktop or Web users have. Sometimes, a mobile device can benefit from a handy button or two. Make sure they fit the user's purpose. I didn't need to design buttons for my super simple app, Overheard Word. For most app users, the up-and-down swipe navigation I used is intuitive.

In this article, I gave a general introduction to mobile app deployment and didn't go into depth about testing because you haven't deployed a production app yet. Most users won't install an app without a good reputation, which is why many successful apps are distributed through intermediaries such as Google Play, the iTunes App Store, or the Amazon Appstore for Android. In this article, I detailed the process of meeting app store security standards, which requires only a few steps and is much simpler than signing your app. The important thing is that once your app is published by a major distributor, it becomes available to the world.

<<:  Android Wear software review: Not bad for smartphone companion software

>>:  The 4 levels of training for software engineers in Silicon Valley

Recommend

4 common senses of short video operation!

In the past few years, I have shared some persona...

The "Time Cinema" of the dinosaur era, this is Lufeng, Yunnan!

The discovery of the Lufeng ancient vertebrate fa...

Can we distinguish different leopards by their calls? | Nature Trumpet

This is the 73rd issue of the Nature Trumpet colu...

You must pay attention to these issues when upgrading to Windows 10

Microsoft's Windows 10 operating system will ...

How to promote an event without money or resources?

How to run an event without money and resources? ...

New brand, IP marketing methodology that is unwilling to be disclosed!

The global economy is recovering and growing in 2...

APP strategy analysis: should we make a small program or a big portal?

(one) Many of today’s Internet products are actua...

Why do I advise you to lose weight in winter? Because the effect is so good

As soon as winter comes, men, women, old and youn...

Peipei's Dad "Pepei's Dad's Thinking Forum"

Peipei Dad's "Pepei Dad's Thinking F...