Click here to Skip to main content
13,149,756 members (81,418 online)
Click here to Skip to main content
Add your own
alternative version

Stats

18.4K views
216 downloads
11 bookmarked
Posted 8 Oct 2015

Wearable Chess (Conquering Android Wear, Part 1)

, 14 Oct 2015
Rate this:
Please Sign up or sign in to vote.
Introducing Wearable Chess - a complete, open-source chess app for Android Wearables!

Get it on Google Play

 

Introduction

Hello and welcome to Wearable Chess!

Ever since I began programming, I have dreamt of creating a chess app - ideally powered by a chess engine of my own creation. Unfortunately, although I have tried many times to create my own chess AI, every attempt so far has failed for various reasons. The closest I ever came to a true chess AI was an engine that could enforce the rules - minus castling & en-passant - and do a very simple (and slow!) 1-ply search.

Am I going to write my own chess AI someday? Probably, yes... but not today and not for this article. Instead, this app is powered by Toledo Nanochess, an incredibly compact engine created by Óscar Toledo Gutiérrez.

I learnt a lot while building Wearable Chess, and in this article, I am going to try to share what I learnt - from installing Android Studio to publishing the finished app on Google Play.

Please note: When I first showed people this app, a few of them were concerned that the chessboard would be too difficult to control. However, once they had a chance to play with the app, they were pleasantly surprised by how easy it actually was - in fact I myself am a little surprised at how easy the board is to control :-)

If you have any questions while reading through this article, feel free to post them in the comments section at the bottom of the page. Enjoy!

Helpful Prerequisites

  • Some experience with Java
    ...but if you haven't used Java before, don't worry. Java is relatively easy to learn on the fly, especially if (as in my case) you're already comfortable using a similiar language like C#.
  • GIMP
    You could also use Inkscape. At very least you will need some sort of image editing software to create game assets and icons.
  • A Google Play Developer Account (costs $25)
    If you want to publish your apps, you will need to buy one of these. Google Play is awesome because this is a once-off cost - you don't have to pay yearly fees to remain a developer.
  • A real device for debugging
    Athough not strictly necessary, a real device definitely helps a lot. If you don't have one, though, you can use AVDs.

Setting up our Dev enviroment

To ensure that complete beginners are not left behind in this article, this first section will explain some of the fundamental concepts of Android Wear development. If you already have some familiarity with Android development, then you may want to skip this part :-)

Android Studio

Once upon a time, the only way to develop native Android apps was with Eclipse, but recently Google has released an official, tailored IDE for Android called Android Studio. I highly recommend that you use Android Studio when building modern Android apps, mostly because it has been designed with modern devices like wearables in mind, and requires much less tinkering when you are a beginner.

Head over to Google to download and install Android Studio.

Creating AVDs

 

AVD's (Android Virtual Devices) allow you to test and debug your apps on your computer without having to connect a real device. Because they are locally contained, they are often faster than real devices at accepting and installing new builds. When you are using a real device, you have to wait for each build to compile and install via bluetooth.

To create an Android Wear AVD, go to Tools>>Android>>AVD Manager in Android Studio, then click on "Create Virtual Device" in the lower left corner of the window. Select "Wear" in the tabs on the left of the window, choose a type in the middle of the screen, then follow the wizard through the rest of the way. I recommend enabling GPU acceleration for your AVD - it will make the AVD much more responsive.

Debugging on real devices

This can get tricky. To configure your Android Wear watch for debugging you must...

  1. Ensure that you have Developer Mode enabled on both your watch and your handset.
    Developer Mode can be found under "System" in the settings app. If you can't see "Developer Options" under System Settings, go to "About" instead, scroll down and tap repeatedly on the "Build Number" field until you get a notification saying that you have unlocked Developer features. This process applies to both your handset and your watch.
  2. Connect your handset to your computer via USB.
  3. Make sure that your handset is connected as a "Camera" and not as a "Media Device".
    It took me a long time to figure out that this was necessary!
  4. Open command prompt and navigate to the platform-tools directory of your Android SDK. I do this by using this command: "cd C:\Program Files (x86)\Android\android-sdk\platform-tools"
  5. To double check that your handset is configured correctly, enter the command "adb devices"
  6. If your handset is not listed after you run that command, then you need to install an ADB driver for your handset. Try googling for the official one, or check out this fantastic Universal ADB Driver by koush.
  7. Once you can see your handset listed after running "adb devices", open the Android Wear companion app on your handset, then tap the gear icon to open the settings. Turn on "Debugging over Bluetooth". Leave the settings activity open on your handset.
  8. Open Developer Options on your Watch and enable "ADB debugging" and "Debug over Bluetooth".
  9. If all has gone well, you should see "Host: disconnected, Target: connected" on your handset.
  10. Lastly, return to command prompt on your PC and type these exact commands:
    adb forward tcp:4444 localabstract:/adb-hub
    adb connect localhost:4444

That's it! You are now ready to build and debug apps on your Android Watch!

Anyway, moving on to how Wearable Chess was built...

Working with Toledo Nanochess

If you haven't heard about Toledo Nanochess before, prepare to be amazed. Here is the entire compressed (and highly unreadable ;P) source code of the Java edition:

//(c)2010 Oscar Toledo G.
import java.applet.*;import java.awt.event.*;import java.awt.*;
public class toledo_chess extends Applet implements MouseListener{
int B,i,y,u,b,I[]=new int[411],G=120,l[]={5,3,4,6,2,4,3,5,1,1,1,1,1,1,1,1,9,9,9
,9,9,9,9,9,13,11,12,14,10,12,11,13,0,11,0,34,33,55,94,0,1,2,3,3,2,1,0,-1,1,-10,
10,-11,-9,9,11,10,20,-9,-11,-10,-20,-21,-19,-12,-8,8,12,19,21,53,47,61,51,47,47
};Image[]im;static final int x=10,z=15,M=10000;
public void init(){for(B=i=y=u=b=0;B++<120;)I[B-1]=B%x!=0?B/x%x<2|B%x<2?7:(B/x&
4)!=0?0:l[i++]|16:7;im=new Image[16];for(;b<15;b++)if(b<7|b>8)im[b]=this.
getImage(this.getDocumentBase(),b+".gif");addMouseListener(this);}
public void stop(){removeMouseListener(this);}
public void update(Graphics g){paint(g);}
public void paint(Graphics g){int x,y,c=21,a;boolean n=false;for(y=0;y<320;y+=
40){for(x=0;x<320;x+=40){g.drawImage(im[I[c]&z],x,y,40,40,n?new Color(144,144,
208):new Color(192,192,255),this);if(c==B){g.setColor(new Color(255,255,0));g.
drawRect(x,y,39,39);g.drawRect(x+1,y+1,37,37);}c++;n=!n;}c+=2;n=!n;}}
void Z(){paint(getGraphics());}
public void mouseExited(MouseEvent e){}public
void mousePressed(MouseEvent e){}public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseReleased(MouseEvent e){int s=e.getX()/40+(e.getY()/40*x+21);i=
(I[s]^y)&z;if(i>8){B=s;Z();}else if(B!=0&&i<9){b=s;i=I[B]&z;
if((i&7)==1&(b<29|b
>90))i=14^y;X(0,0,0,21,u,1);B=0;Z();if(y>0){X(0,0,0,21,u,4/*ply*/);X(0,0,0,21,u
,1);B=0;Z();}}}
public String getAppletInfo(){return"Toledo Java Chess by Oscar Toledo G.";}
int X(int w,int c,int h,int e,int S,int s){int t,o,L,E,d,O=e,N=-M*M,K=78-h<<x,p a="y">
0?-x:x;boolean D,J;y^=8;G++;D=w>0||s>0&&s>=h&&X(0,0,0,21,0,0
)>M;do{if((o=I[p=O])>0){q=o&z^y;if(q<7){A=(q--&2)>0?8:4;
C=(o&z)!=9?l[69+q]:57;
do{r=I[p+=l[C]];if(w<1|p==w){g=q>0|p+a!=S?0:S;
if(r<1&(q>0|A<3||g>0)||(r+1&z^y)>
9&&q>0|A>2){if((r&7)==2){y^=8;I[G--]=O;return K;}
m=0;n=o&z;J=true;E=I[p-a]&z;if
(q>0|E!=7)t=n;else{n+=2;t=6^y;}while(n<=t){L=(r>0?l[r&7|32]*9-h-q:0);
if(s>0)L+=
(q!=1?l[p/x+37]-l[O/x+37]+l[p%x+38]-l[O%x+38]+o/16*8:(m>0?9:0))+(q<1?l[p%x+38]-
1+((I[p-1]^n)<1?1:0)+((I[p+1]^n)<1?1:0)+l[n&7|32]*9-99+(g>0?99:0)+(A<2?1:0):
0)+((E^y^9)<1?1:0);if(s>h||1<s&s==h&&l>z|D){I[p]=n;I[O]=0;if(m>0){I[g]=I[m];I[m]=0
;}else if(g>0)I[g]=0;L-=X(s>h|D?0:p,L-N,h+1,I[G+1],E=q>0|A>1?0:p,s);
if(h<1&s==1&&B==O&i==n&p==b&L>-M)
G--;return u=E;}J=q!=1||A<7||m>0||s<1|D|r>0|o<z||x(0,0,0>M;I[O]=o;I[p]=r;
if(m>0){I[m]=I[g];I[g]=0;}else if(g>0)I[g]=9^y;}if(L>N){I[G]=O;if(s>1)
{if(h>0&&c-L<0){y^=8;G--;return L;}if(h<1){i=n;B=O;b=p;}}N=L;}
if(J)n++;else{g=p;m=p<o?g-3:g+2;if(i[m]<z|i[m+o-p]>0||I[p+=p-O]>0)n++;}}}}}
while(r<1&q>2||((p=O)>0)&&(q>0|A>2|o>z&r<1)
&&++C>0&&--A>0);}}if(++O>98)O=20;}
while(e!=O);y^=8;G--;return N!=-M*M&&N>-K+1924|D?N:0;}}
</o?g-3:g+2;if(i[m]<z|i[m+o-p]></z||x(0,0,0></s&s==h&&l></x,p>

That's it! The above code can play a complete, intelligent game of chess, as well as render a chessboard and accept move input. In fact. if you head over to nanochess.org, you will see that the author of this code also has several Javascript versions that are about half the size of what you see above. Very impressive stuff.

So how did I reconfigure this to work within my app? The first thing I did was drop the above source code into an online code beautifier, which resulted in this:

//(c)2010 Oscar Toledo G.
import java.applet.*;
import java.awt.event.*;
import java.awt.*;
public class toledo_chess extends Applet implements MouseListener {
	int B, i, y, u, b, I[] = new int[411], G = 120, l[] = {
		5, 3, 4, 6, 2, 4, 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 13, 11, 12,
		14, 10, 12, 11, 13, 0, 11, 0, 34, 33, 55, 94, 0, 1, 2, 3, 3, 2, 1, 0, -1, 1, -10,
		10, -11, -9, 9, 11, 10, 20, -9, -11, -10, -20, -21, -19, -12, -8, 8, 12, 19, 21, 53,
		47, 61, 51, 47, 47
	};
	Image[] im;
	static final int x = 10, z = 15, M = 10000;
	public void init() {
		for (B = i = y = u = b = 0; B++ < 120;) I[B - 1] = B % x != 0 ? B / x % x < 2 |
		    B % x < 2 ? 7 : (B / x & 4) != 0 ? 0 : l[i++] | 16 : 7;
		im = new Image[16];
		for (; b < 15; b++) if (b < 7 | b > 8) im[b] = this.
		getImage(this.getDocumentBase(), b + ".gif");
		addMouseListener(this);
	}
	public void stop() {
		removeMouseListener(this);
	}
	public void update(Graphics g) {
		paint(g);
	}
	public void paint(Graphics g) {
		int x, y, c = 21, a;
		boolean n = false;
		for (y = 0; y < 320; y += 40) {
			for (x = 0; x < 320; x += 40) {
				g.drawImage(im[I[c] & z], x, y, 40, 40, n ? new Color(144, 144,
				208) : new Color(192, 192, 255), this);
				if (c == B) {
					g.setColor(new Color(255, 255, 0));
					g.
					drawRect(x, y, 39, 39);
					g.drawRect(x + 1, y + 1, 37, 37);
				}
				c++;
				n = !n;
			}
			c += 2;
			n = !n;
		}
	}
	void Z() {
		paint(getGraphics());
	}
	public void mouseExited(MouseEvent e) {}
	public
	void mousePressed(MouseEvent e) {}
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {
		int s = e.getX() / 40 + (e.getY() / 40 * x + 21);
		i = (I[s] ^ y) & z;
		if (i > 8) {
			B = s;
			Z();
		} else if (B != 0 && i < 9) {
			b = s;
			i = I[B] & z;
			if ((i & 7) == 1 & (b < 29 | b > 90)) i = 14 ^ y;
			X(0, 0, 0, 21, u, 1);
			B = 0;
			Z();
			if (y > 0) {
				X(0, 0, 0, 21, u, 4 /*ply*/ );
				X(0, 0, 0, 21, u, 1);
				B = 0;
				Z();
			}
		}
	}
	public String getAppletInfo() {
		return "Toledo Java Chess by Oscar Toledo G.";
	}
	int X(int w, int c, int h, int e, int S, int s) {
		//EDIT: Removed for clarity in this article.
        //The code that was here is near-impossible to read, even when beautified.
	}
}

That's a bit easier to read! :-)

We can now see that this code is broken into several major functions:

  • X(int w, int c, int h, int e, int S, int s)
    This heavily obfuscated function does almost all of the heavy lifting, including alpha-beta cutting and move validation.
  • mouseReleased(MouseEvent e)
    This function is what makes everything in this code "happen". We can see at a glance that it controls which pieces move and when.
  • paint(Graphics g)
    This function draws a simple chessboard.
  • init()
    This function sets up every new game and generally prepares everything for action.

With this knowledge under my belt, I created a class called Chess.java that allowed me to implement features like highlighting legal moves, undoing moves, etc. Here it is:

/**
 * Created by mitch on 27/09/2015.
 * Enormous amounts of this code are from Toledo Nanochess (nanochess.org)
 * This code is free for non-commercial use, but for commercial use please contact the author of Nanochess using the URL above.
 */
public class Chess {
    public int B, i, y, u, b, L, I[] = new int[411], G = 120, l[] = {
            5, 3, 4, 6, 2, 4, 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 13, 11,
            12, 14, 10, 12, 11, 13, 0, 11, 0, 34, 33, 55, 94, 0, 1, 2, 3, 3, 2, 1, 0, -1, 1, 
            -10, 10, -11, -9, 9, 11, 10, 20, -9, -11, -10, -20, -21, -19, -12, -8, 8, 12, 19,
            21, 53, 47, 61, 51, 47, 47
    };
    static final int x = 10, z = 15, M = 10000;

    //This is basically the init() function, minus the UI stuff.
    public Chess() {
        for (B = i = y = u = b = 0; B++ < 120;) I[B - 1] = B % x != 0 ? B / x % x < 2 |
            B % x < 2 ? 7 : (B / x & 4) != 0 ? 0 : l[i++] | 16 : 7;
    }
    
    //Once a starting square has been selected, this function tries every square on the board
    //to see whether a move there is legal.
    public ArrayList<<integer> getLegalMoves(int s) {
        ArrayList<integer> validSq = new ArrayList<>();
        for (int j = 21; j < 99 && j != 29; j += j > 90 ? -69 : 10) {
            Chess verifier = new Chess();
            verifier.CopyBoard(this);
            if (verifier.Move(s, j))
                validSq.add(j);
        }
        return validSq;
    }

    //Used to determine whether a capture has taken place!
    public int CountPieces() {
        int count = 0;
        for (int j : I){
            if (j != 0 && j != 7 && j != 8 && j != 15)
                ++count;
        }
        return count;
    }
    
    //Allows this instance of Chess to copy the board of another.
    public void CopyBoard(Chess c) {
        I = c.I.clone(); B = c.B; i = c.i; y = c.y; u = c.u; L = c.L; b = c.b;
    }

    //If the move is legal, it is played and the function returns true,
    //Otherwise the board remains unchanged, and the function returns false.
    public boolean Move(int s, int f) {
        MovePart(s);
        return MovePart(f);
    }

    public boolean MovePart(int s) {
        i = (I[s] ^ y) & z;
        if (i > 8) {
            B = s;
        }
        else if (B != 0 && i < 9) {
            b = s;
            i = I[B] & z;
            if ((i & 7) == 1 & (b < 29 | b > 90)) i = 14 ^ y;
            X(0, 0, 0, 21, u, 1);
            L = B; //Highlight last moves
            B = 0;
            return (y > 0); // If the move was accepted and performed as legal, 
                            // y will be > 0. Don't ask me Y :D
        }
        return false;
    }

...

    public void Respond(int depth) {
        X(0, 0, 0, 21, u, depth /*ply*/ );
        X(0, 0, 0, 21, u, 1);
        L = B; //Highlight last moves
        B = 0;
    }

    int X(int w, int c, int h, int e, int S, int s) {
        ...
    }
}

Toledo Nanochess represents the board with something called a 10x12 array representation. I don't have time to fully explain it here, but basically it means that the each square on the chessboard has a number assigned to it, such that the a8 square is 21, the b8 square is 22, so on so that the h8 square is 28. When you step back down and across to a7, however, you add three to get 31. b7 is 32, c7 is 33, and so on, adding 3 whenever you go down a row on the chessboard.

This is why the for statement in the getLegalMoves() function has such odd parameters.

With this Chess.java class, implementing undo moves was as easy as keeping an ArrayList of Chess objects and restoring them as needed:

public void StoreToHistory() {
    if (history_index < history.size()) {
        Chess record = new Chess();
        record.CopyBoard(toledo);
        history.set(history_index, record);
    }
    else {
        Chess record = new Chess();
        record.CopyBoard(toledo);
        history.add(record);
    }
    ++history_index;
}

public void Takeback(int steps) {
    if (history_index - steps > 0)
        toledo = history.get(history_index -= steps);
    Z();
}

Building the rest of Wearable Chess

The rest of Wearable chess was relatively straightforward to create. I'll list a few of the more notable parts.

Game Graphics - Where Did I Get Them?

To create wooden game graphics, I first searched for free images of wood, then found an image with heaps of variety, and lastly choose some useful areas using GIMP.

The Stauton chess pieces are from WikiMedia.

Drawing the Chessboard

I had to re-write the chessboard renderering part to work with Android Wear. Here is my version, commented and explained:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

	//Essential variables
    double x, y;
    int c = 21;
    boolean n = false;
    width = canvas.getWidth();

    double tile_width = ((double)width / 8.0);
    for (y = 0; y < width; y += tile_width) {
        for (x = 0; x < width; x += tile_width) {
			//Get the rectangle of the square
            Rect r_sq = new Rect((int) x, (int) y, (int) (x + tile_width),
                (int) (y + tile_width));
			
			//Light or Dark square?
            pSquare.setColor(n ? Color.argb(255, 144, 144, 208) :
                Color.argb(255, 192, 192, 255));
            canvas.drawBitmap(n ? b_sq_black : b_sq_white, new Rect(0, 0,
                b_sq_white.getWidth(), b_sq_white.getHeight()), r_sq, pSquare);

            if (Highlights) {
				//Previous moves
                if (c == toledo.b || c == toledo.L) {
                    pSquare.setColor(Color.argb(120, 0, 0, 180));
                    canvas.drawRect(r_sq, pSquare);
                }
				
				//Is it a valid square to move to?
                if (validSq.indexOf(c) != -1) {
                    pSquare.setColor(Color.argb(120, 0, 180, 0));
                    canvas.drawRect(r_sq, pSquare);
                }
            }
			
			//Selected square?
            if (c == toledo.B) {
                pOutline.setStrokeWidth(1);
                pOutline.setColor(Color.argb(255, 255, 255, 0));
                canvas.drawRect((int)(x + 1), (int)(y + 1), (int)(x + tile_width),
                    (int)(y + tile_width), pOutline);
            }
			
			//Draw the piece
            pSquare.setColor(Color.argb(255, 0, 0, 0));
            canvas.drawBitmap(im[toledo.I[c] & z], new Rect(0, 0,
                im[toledo.I[c] & z].getWidth(), im[toledo.I[c] & z].getHeight()),
                r_sq, pSquare);
			
            c++;
            n = !n;
        }
        c += 2;
        n = !n;
    }
	
    is_drawing = false;
}

Creating the Menu

The Menu is implemented using something called a GridViewPager, which basically allows swipable, full-screen fragments to occupy the screen one-at-a-time. In this case, I have one fragment containing the ChessboardView, and another Fragment containing the menu.

Creating a menu like this could be a whole article of it's own - which perhaps I will write someday. For now, though, here is the snippet of code from SettingsFragment.java that stitches the whole menu together:

String[] headers = { "Takeback", "New Game", "Difficulty",
    "Style", "Highlights", "Vibrations", "About" };
String[] subheaders = { "Undo last move", "Start a new game",
    "4 (Normal)", "Staunton", "On", "Off",
    "About this app"};
int[] resources = { R.drawable.ic_undo_black_24dp, R.drawable.white_king_st,
    R.drawable.brain, R.drawable.black_knight_st, R.drawable.highlights,
    R.drawable.vibration_on, R.drawable.ic_info_outline_black_24dp};

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
	...
	
    SharedPreferences settings = rootView.getContext().getSharedPreferences(PREFS_NAME, 0);
    boolean Vibration = settings.getBoolean("vibration", false);
    boolean Highlights = settings.getBoolean("highlights", true);
    subheaders[4] = Highlights ? "On" : "Off";
    subheaders[5] = Vibration ? "On" : "Off";

    WearableListView listView = (WearableListView) rootView.findViewById(R.id.wearable_list);
    listView.setAdapter(new CustomListAdapter(rootView.getContext(), headers, subheaders,
        resources));
    listView.setClickListener(this);
    listView.setGreedyTouchMode(true);

    ...
}

Enabling Hold-to-Dismiss

By default, all Android apps are closed by swiping to the left. Because I wanted to avoid accidentally closing the app during a game, I decided to implement the Google-recommended alternative: hold-down to close.

To do this yourself in your own apps, create a new resource file called hold_to_exit.xml:

<resources>
    <style name="HoldToExit" parent="@android:style/Theme.DeviceDefault.Light">
        <item name="android:windowSwipeToDismiss">false</item>
    </style>
</resources>

Then, in AndroidManifest.xml, set the style of your MainActivity to be "HoldToExit" like so:

android:theme="@style/HoldToExit"

Lastly, add this to the java file of your MainActivity:

private DismissOverlayView mDismissOverlay;
private GestureDetector mDetector;
...

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    mDismissOverlay = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
    mDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
        public void onLongPress(MotionEvent ev) {
            mDismissOverlay.show();
        }
    });
    ...
}
    
@Override
public boolean dispatchTouchEvent (MotionEvent e) {
    return mDetector.onTouchEvent(e) || super.dispatchTouchEvent(e);
}

FAQ - Frequently Asked Questions

Before closing this article, I thought I'd include a generic FAQ section for beginners learning to build Android Wear apps. If you have a question that is not listed here, feel free to post it in the comments section beneath this article Smile | :)

How do I create a new Android Wear project?

Open Android Studio and go to File>>New>>New Project. Ater naming your app, click "Next", and in the resulting window place a tick in the box next to "Wear". Complete the rest of the wizard with the default settings.

What are Resources and how do I use them?

Android resources are all contained within the "res" folder of you new project, and are used mostly for UI purposes. They can be everything from Images to Strings to Animations to Layouts.

For example, a blank Android Wear project will have a resource file called activity_main.xml, which looks like this:

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout

    xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto"

    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"

    android:layout_height="match_parent" android:id="@+id/container" tools:context=".MainActivity"

    tools:deviceIds="wear">

    <TextView android:id="@+id/text" app:layout_box="all" android:layout_width="match_parent"

        android:layout_height="match_parent" android:text="@string/hello_world" />

    <TextView android:id="@+id/clock" app:layout_box="all" android:layout_gravity="bottom|start"

        android:layout_width="wrap_content" android:layout_height="wrap_content"

        android:textColor="@android:color/white" />

</android.support.wearable.view.BoxInsetLayout>

There is nothing magical about this XML file - it is simply another static resource, just like an image or an icon. Its only importantance lies in the fact that MainActivity.java inflates its layout using this resource:

//The "actual" activity is defined here.
//It is possible to create Activities, Views, and Fragments without using an XML resource.

public class MainActivity extends WearableActivity {

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); //References activity_main.xml
        
        ...
    }
    
    ...
}

Resources have many uses - for example, it is considered good practice to store large string values in a resource file called strings.xml, rather than hard typing them into your Java code or your layout XML resources.

What are the mhdpi, xxxhdpi, etc folders for?

These are meant to help you scale your app cleanly across screens with differing pixel densities.

For more information, see this page from the official Android Developer site.

How do I create a new activity?

The easiest way is to use Android Studio's wizard. In Android Studio, right-click on the res/layouts folder in the projects explorer, then go to New>>Activity>>Always-On Wear Activity.

 

To create an activity manually, just create a new java class and make it extend the WearableActivity class:

package ...;

import android.support.wearable.activity.WearableActivity;

public class SomeCustomActivity extends WearableActivity {
    
}

Don't forget to declare it in your AndroidManifest.xml file, either: (otherwise you will get an error when you try to use your activity)

<activity

    android:name=".SomeCustomActivity"

    android:label="@string/title_activity_..."

    android:theme="@android:style/Theme.DeviceDefault.Light" >
</activity>

What things should I consider when building an Android Wear app?

You should focus primarily on top-notch performance & minimal battery usage.

Because Android Smartwatches are still, well, watches, we as developers should definitely avoid wasting our users' time and battery. The ideal Android Wear app should be light, simple, and easy to use on the fly.

My device won't connect to ADB! What can I do?

Maintaining a stable connection with a real Android Wear watch can sometimes be difficult. Several things I have tried include:

  • Rebooting the PC/Handset/Watch. You can reboot the handset witht the adb command "reboot".
  • Running adb disconnect, then reconnecting by forwarding the TCP port, etc. (see "Debugging on Real Devices" above).
  • Switching off debugging mode on the devices, then turning it back on.
  • Updating the ADB driver

Some combination of the above has fixed every single issue I've had so far.

How do I publish my app?

You will need:

  • A logo/icon
  • Some screenshots
  • A Google Play Developer Account

Once you have those, make sure that the package name is identical for your wearable app and your handset app. Ensure that both apps have exactly the same permissions declared in their Manifest files.

Then, in Android Studio, go to Build>>Generate Signed APK and follow the wizard to create your release-ready .APK files. You will then be able to upload your app to Google Play through the developer console.

Where can I get a Google Play Developer Account?

From right here. It costs $25 USD, but this is only a once-off cost.

Are there any traps I should look out for when publishing?

Yes! Make sure that your wearable app is correctly packaged inside your mobile app.

When I first tried publishing Wearable Chess, Google Play rejected it from the Wearables section because I had forgotten to match the required permissions between the handset and wear apps. As a result, the wearable app was undetectable.

Conclusion

Thanks a lot for reading to the end! Building this app was somewhat of a journey for me, and I'm glad I've been able to share the story with you :D

Would you like to read more? Click here read the next article in my Conquering Android Wear series.

As always - if you have any comments, questions, or suggestions of any kind, please post them below. I'd love to hear from you.

BTW, if you have an Android Wear smartwatch, why not head over to Google Play and get Wearable Chess for free:

Get it on Google Play

If you like the app, don't forget to give it five stars and share it with your friends! :cool:

History

  • 9/10/15 Published the first version of Wearable Chess 
  • 15/10/15 Published a small update. Explained the menu a bit more, corrected some typos and linked to the second article in this series.
  • 16/10/15 Fixed the formatting of the Toledo Chess Code & made some other small changes. Thanks Nelek!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Mitchell J.
Founder
Australia Australia
I'm a university student with a passion for stunning UI design and flawless code.

I fell in love with computers when I was 10 years old, and today I am fluent in C#, Python, Java, Javascript, HTML5, and CSS3. I know a little MATLAB, PHP, and SQL.

In my spare time, I build all sorts of cool things, like this Rubik's Cube Robot.

Away from the keyboard, I enjoy quality time with friends and family, as well as reading, painting, playing the piano, teaching chess & karate, and volunteering within my community.

I have learnt that success comes through hard work and dedication, and that giving back to the same people and communities that have helped me is both important and very rewarding.

Follow me on GitHub

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
raddevus1-Mar-16 7:21
memberraddevus1-Mar-16 7:21 
GeneralMy vote of 5 Pin
Santhk21-Oct-15 21:48
professionalSanthk21-Oct-15 21:48 
GeneralRe: My vote of 5 Pin
Mitchell J.22-Oct-15 2:11
professionalMitchell J.22-Oct-15 2:11 
GeneralMy vote of 5 Pin
Agent__00715-Oct-15 18:57
professionalAgent__00715-Oct-15 18:57 
GeneralRe: My vote of 5 Pin
Mitchell J.16-Oct-15 2:26
professionalMitchell J.16-Oct-15 2:26 
QuestionSnippet Pin
Nelek15-Oct-15 4:25
protectorNelek15-Oct-15 4:25 
AnswerRe: Snippet Pin
Mitchell J.15-Oct-15 4:33
professionalMitchell J.15-Oct-15 4:33 
GeneralRe: Snippet Pin
Nelek15-Oct-15 4:49
protectorNelek15-Oct-15 4:49 
GeneralRe: Snippet Pin
Mitchell J.15-Oct-15 4:58
professionalMitchell J.15-Oct-15 4:58 
GeneralRe: Snippet Pin
Nelek15-Oct-15 5:21
protectorNelek15-Oct-15 5:21 
GeneralRe: Snippet Pin
Mitchell J.15-Oct-15 5:52
professionalMitchell J.15-Oct-15 5:52 
GeneralRe: Snippet Pin
Nelek15-Oct-15 11:32
protectorNelek15-Oct-15 11:32 
GeneralRe: Snippet Pin
Mitchell J.15-Oct-15 15:05
professionalMitchell J.15-Oct-15 15:05 
GeneralRe: Snippet Pin
Nelek15-Oct-15 23:44
protectorNelek15-Oct-15 23:44 
GeneralVery good! Pin
Kornfeld Eliyahu Peter10-Oct-15 20:21
mvpKornfeld Eliyahu Peter10-Oct-15 20:21 
GeneralRe: Very good! Pin
Mitchell J.10-Oct-15 21:02
professionalMitchell J.10-Oct-15 21:02 
GeneralMy vote of 5 Pin
BillWoodruff8-Oct-15 23:47
mvpBillWoodruff8-Oct-15 23:47 
GeneralRe: My vote of 5 Pin
Mitchell J.10-Oct-15 21:01
professionalMitchell J.10-Oct-15 21:01 
GeneralMy vote of 5 Pin
BillWoodruff8-Oct-15 23:47
mvpBillWoodruff8-Oct-15 23:47 
GeneralMy vote of 5 Pin
syed shanu8-Oct-15 17:19
mvpsyed shanu8-Oct-15 17:19 
GeneralRe: My vote of 5 Pin
Mitchell J.8-Oct-15 17:33
professionalMitchell J.8-Oct-15 17:33 
GeneralThanks for the contest entry Pin
Kevin Priddle8-Oct-15 10:26
staffKevin Priddle8-Oct-15 10:26 
GeneralRe: Thanks for the contest entry Pin
Mitchell J.8-Oct-15 15:04
professionalMitchell J.8-Oct-15 15:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.170924.2 | Last Updated 14 Oct 2015
Article Copyright 2015 by Mitchell J.
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid