Merge "Hard reset implemented"
diff --git a/go/src/hearts/assets/RejoinPressed.png b/go/src/hearts/assets/RejoinPressed.png
new file mode 100644
index 0000000..98dcfbd
--- /dev/null
+++ b/go/src/hearts/assets/RejoinPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/RejoinUnpressed.png b/go/src/hearts/assets/RejoinUnpressed.png
new file mode 100644
index 0000000..54fc684
--- /dev/null
+++ b/go/src/hearts/assets/RejoinUnpressed.png
Binary files differ
diff --git a/go/src/hearts/img/texture/texture.go b/go/src/hearts/img/texture/texture.go
index 9e3fe92..a8281e4 100644
--- a/go/src/hearts/img/texture/texture.go
+++ b/go/src/hearts/img/texture/texture.go
@@ -278,7 +278,7 @@
 		"PassPressed.png", "PassUnpressed.png", "RightArrowBlue.png", "LeftArrowBlue.png", "AcrossArrowBlue.png", "RightArrowGray.png",
 		"LeftArrowGray.png", "AcrossArrowGray.png", "TakeTrickTableUnpressed.png", "TakeTrickTablePressed.png", "TakeTrickHandPressed.png",
 		"TakeTrickHandUnpressed.png", "android.png", "cat.png", "man.png", "woman.png", "TakeUnpressed.png", "TakePressed.png",
-		"UnplayedBorder1.png", "UnplayedBorder2.png",
+		"UnplayedBorder1.png", "UnplayedBorder2.png", "RejoinPressed.png", "RejoinUnpressed.png",
 	}
 	for _, f := range boundedImgs {
 		a, err := asset.Open(f)
diff --git a/go/src/hearts/img/view/view.go b/go/src/hearts/img/view/view.go
index 5600864..3066645 100644
--- a/go/src/hearts/img/view/view.go
+++ b/go/src/hearts/img/view/view.go
@@ -9,7 +9,9 @@
 package view
 
 import (
+	"bufio"
 	"fmt"
+	"os"
 	"sort"
 	"strconv"
 
@@ -20,6 +22,7 @@
 	"hearts/img/texture"
 	"hearts/img/uistate"
 	"hearts/logic/card"
+	"hearts/util"
 
 	"golang.org/x/mobile/exp/f32"
 	"golang.org/x/mobile/exp/sprite"
@@ -91,7 +94,6 @@
 }
 
 // Waiting view: Displays the word "Waiting". To be displayed when players are waiting for a new round to be dealt
-// TODO(emshack): Integrate this with Arrange view and Score view so that a separate screen is not necessary
 func LoadWaitingView(u *uistate.UIState) {
 	u.M.Lock()
 	defer u.M.Unlock()
@@ -121,6 +123,37 @@
 	newGamePos := coords.MakeVec((u.WindowSize.X-newGameDim.X)/2, u.TopPadding)
 	u.Buttons["newGame"] = texture.MakeImgWithAlt(newGameImg, newGameAlt, newGamePos, newGameDim, true, u)
 	buttonNum := 1
+	file, err := os.OpenFile(util.AddrFile, os.O_RDONLY|os.O_CREATE, 0666)
+	if err != nil {
+		fmt.Println("err:", err)
+	}
+	scanner := bufio.NewScanner(file)
+	var oldAddr string
+	scanner.Scan()
+	addr := scanner.Text()
+	// Search through all the database's syncgroups to see if addr represents a current syncgroup
+	app := u.Service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
+	allAddrs, _ := db.GetSyncgroupNames(u.Ctx)
+	fmt.Println(allAddrs)
+	for _, a := range allAddrs {
+		if a == addr {
+			oldAddr = a
+		}
+	}
+	file.Close()
+	if oldAddr != "" {
+		rejoinGameImg := u.Texs["RejoinUnpressed.png"]
+		rejoinGameAlt := u.Texs["RejoinPressed.png"]
+		rejoinGameDim := coords.MakeVec(4*u.CardDim.X, u.CardDim.Y)
+		rejoinGamePos := coords.MakeVec((u.WindowSize.X-rejoinGameDim.X)/2, u.TopPadding+rejoinGameDim.Y+u.Padding)
+		u.Buttons["rejoinGame"] = texture.MakeImgWithAlt(rejoinGameImg, rejoinGameAlt, rejoinGamePos, rejoinGameDim, true, u)
+		scanner.Scan()
+		creator := scanner.Text()
+		bInfo := oldAddr + "|" + creator
+		u.Buttons["rejoinGame"].SetInfo(bInfo)
+		buttonNum = 2
+	}
 	for _, d := range u.DiscGroups {
 		if d != nil {
 			dataMap := d.GameStartData
@@ -146,7 +179,9 @@
 				joinGameAlt := u.Texs["JoinGamePressed.png"]
 				joinGamePos := coords.MakeVec(u.WindowSize.X-u.BottomPadding-newGameDim.X, newGamePos.Y+float32(buttonNum)*(bgBannerDim.Y+u.Padding))
 				u.Buttons[fmt.Sprintf("joinGame-%d", buttonNum)] = texture.MakeImgWithAlt(joinGameImg, joinGameAlt, joinGamePos, newGameDim, true, u)
-				u.Buttons[fmt.Sprintf("joinGame-%d", buttonNum)].SetInfo(d.LogAddr)
+				creator := creatorName == util.UserName
+				bInfo := d.LogAddr + "|" + strconv.FormatBool(creator)
+				u.Buttons[fmt.Sprintf("joinGame-%d", buttonNum)].SetInfo(bInfo)
 				buttonNum++
 			}
 		}
@@ -376,19 +411,21 @@
 
 // Decides which view of the player's hand to load based on what steps of the round they have completed
 func LoadPassOrTakeOrPlay(u *uistate.UIState) {
-	p := u.CurTable.GetPlayers()[u.CurPlayerIndex]
-	if u.CurTable.RoundOver() {
-		LoadScoreView(u)
-	} else if p.GetDoneTaking() || u.CurTable.GetDir() == direction.None {
-		if u.CurView == uistate.Play {
-			LoadPlayView(true, u)
+	if u.CurPlayerIndex >= 0 {
+		p := u.CurTable.GetPlayers()[u.CurPlayerIndex]
+		if u.CurTable.RoundOver() {
+			LoadScoreView(u)
+		} else if p.GetDoneTaking() || u.CurTable.GetDir() == direction.None {
+			if u.CurView == uistate.Play {
+				LoadPlayView(true, u)
+			} else {
+				LoadPlayView(false, u)
+			}
+		} else if p.GetDonePassing() {
+			LoadTakeView(u)
 		} else {
-			LoadPlayView(false, u)
+			LoadPassView(u)
 		}
-	} else if p.GetDonePassing() {
-		LoadTakeView(u)
-	} else {
-		LoadPassView(u)
 	}
 }
 
diff --git a/go/src/hearts/main.go b/go/src/hearts/main.go
index 38eb093..d2b31fb 100644
--- a/go/src/hearts/main.go
+++ b/go/src/hearts/main.go
@@ -23,6 +23,7 @@
 	"hearts/logic/table"
 	"hearts/sync"
 	"hearts/touchhandler"
+	"hearts/util"
 
 	"golang.org/x/mobile/app"
 	"golang.org/x/mobile/event/lifecycle"
@@ -86,7 +87,7 @@
 	ctx, shutdown := v23.Init()
 	u.Shutdown = shutdown
 	u.Ctx = ctx
-	u.Service = syncbase.NewService(sync.MountPoint + "/croupier/" + sync.SBName)
+	u.Service = syncbase.NewService(util.MountPoint + "/croupier/" + util.SBName)
 	namespace := v23.GetNamespace(u.Ctx)
 	allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
 	permissions := access.Permissions{
@@ -96,8 +97,8 @@
 		"Resolve": allAccess,
 		"Debug":   allAccess,
 	}
-	namespace.SetPermissions(u.Ctx, sync.MountPoint, permissions, "")
-	namespace.SetPermissions(u.Ctx, sync.MountPoint+"/croupier", permissions, "")
+	namespace.SetPermissions(u.Ctx, util.MountPoint, permissions, "")
+	namespace.SetPermissions(u.Ctx, util.MountPoint+"/croupier", permissions, "")
 	u.Service.SetPermissions(u.Ctx, permissions, "")
 	u.Images = glutil.NewImages(glctx)
 	fps = debug.NewFPS(u.Images)
diff --git a/go/src/hearts/sync/client.go b/go/src/hearts/sync/client.go
index c7f4194..195578a 100644
--- a/go/src/hearts/sync/client.go
+++ b/go/src/hearts/sync/client.go
@@ -11,6 +11,7 @@
 
 	"hearts/img/uistate"
 	"hearts/img/view"
+	"hearts/util"
 
 	"v.io/v23/context"
 	"v.io/v23/discovery"
@@ -30,7 +31,7 @@
 	}
 	ds := ldiscovery.NewWithPlugins([]ldiscovery.Plugin{mdns})
 	fmt.Printf("Start scanning...\n")
-	ch, err := ds.Scan(ctx, fmt.Sprintf("v.InterfaceName = \"%s\"", CroupierInterface))
+	ch, err := ds.Scan(ctx, fmt.Sprintf("v.InterfaceName = \"%s\"", util.CroupierInterface))
 	if err != nil {
 		ctx.Fatalf("Scan failed: %v", err)
 	}
@@ -77,7 +78,7 @@
 
 // Returns a watchstream of the data in the table
 func WatchData(tableName, prefix string, u *uistate.UIState) (nosql.WatchStream, error) {
-	db := u.Service.App(AppName).NoSQLDatabase(DbName, nil)
+	db := u.Service.App(util.AppName).NoSQLDatabase(util.DbName, nil)
 	resumeMarker, err := db.GetResumeMarker(u.Ctx)
 	if err != nil {
 		fmt.Println("RESUMEMARKER ERR: ", err)
@@ -87,21 +88,21 @@
 
 // Returns a scanstream of the data in the table
 func ScanData(tableName, prefix string, u *uistate.UIState) nosql.ScanStream {
-	app := u.Service.App(AppName)
-	db := app.NoSQLDatabase(DbName, nil)
+	app := u.Service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
 	table := db.Table(tableName)
 	rowRange := nosql.Range(prefix, "")
 	return table.Scan(u.Ctx, rowRange)
 }
 
 // Joins gamelog syncgroup
-func JoinLogSyncgroup(logName string, u *uistate.UIState) bool {
+func JoinLogSyncgroup(logName string, creator bool, u *uistate.UIState) bool {
 	fmt.Println("Joining gamelog syncgroup")
-	u.IsOwner = false
-	app := u.Service.App(AppName)
-	db := app.NoSQLDatabase(DbName, nil)
+	u.IsOwner = creator
+	app := u.Service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
 	logSg := db.Syncgroup(logName)
-	myInfoJoiner := wire.SyncgroupMemberInfo{8, false}
+	myInfoJoiner := wire.SyncgroupMemberInfo{8, creator}
 	_, err := logSg.Join(u.Ctx, myInfoJoiner)
 	if err != nil {
 		fmt.Println("SYNCGROUP JOIN ERROR: ", err)
@@ -109,7 +110,7 @@
 	} else {
 		fmt.Println("Syncgroup joined")
 		if u.LogSG != logName {
-			ResetGame(logName, false, u)
+			ResetGame(logName, creator, u)
 		}
 		return true
 	}
@@ -118,8 +119,8 @@
 // Joins player settings syncgroup
 func JoinSettingsSyncgroup(settingsName string, u *uistate.UIState) {
 	fmt.Println("Joining user settings syncgroup")
-	app := u.Service.App(AppName)
-	db := app.NoSQLDatabase(DbName, nil)
+	app := u.Service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
 	settingsSg := db.Syncgroup(settingsName)
 	myInfoJoiner := wire.SyncgroupMemberInfo{8, false}
 	_, err := settingsSg.Join(u.Ctx, myInfoJoiner)
@@ -131,8 +132,8 @@
 }
 
 func NumInSG(logName string, u *uistate.UIState) int {
-	app := u.Service.App(AppName)
-	db := app.NoSQLDatabase(DbName, nil)
+	app := u.Service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
 	sg := db.Syncgroup(logName)
 	members, err := sg.GetMembers(u.Ctx)
 	if err != nil {
diff --git a/go/src/hearts/sync/logWriter.go b/go/src/hearts/sync/logWriter.go
index 110d0dd..b0a8384 100644
--- a/go/src/hearts/sync/logWriter.go
+++ b/go/src/hearts/sync/logWriter.go
@@ -14,6 +14,7 @@
 
 	"hearts/img/uistate"
 	"hearts/logic/card"
+	"hearts/util"
 
 	"v.io/v23/context"
 	"v.io/v23/syncbase"
@@ -95,13 +96,13 @@
 }
 
 func LogPlayerNum(u *uistate.UIState) bool {
-	key := fmt.Sprintf("%d/players/%d/player_number", u.GameID, UserID)
+	key := fmt.Sprintf("%d/players/%d/player_number", u.GameID, util.UserID)
 	value := strconv.Itoa(u.CurPlayerIndex)
 	return logKeyValue(u.Service, u.Ctx, key, value)
 }
 
 func LogSettingsName(name string, u *uistate.UIState) bool {
-	key := fmt.Sprintf("%d/players/%d/settings_sg", u.GameID, UserID)
+	key := fmt.Sprintf("%d/players/%d/settings_sg", u.GameID, util.UserID)
 	return logKeyValue(u.Service, u.Ctx, key, name)
 }
 
diff --git a/go/src/hearts/sync/server.go b/go/src/hearts/sync/server.go
index 68b1147..c618155 100644
--- a/go/src/hearts/sync/server.go
+++ b/go/src/hearts/sync/server.go
@@ -10,10 +10,12 @@
 	"encoding/json"
 	"fmt"
 	"math/rand"
+	"os"
 	"strconv"
 	"strings"
 
 	"hearts/img/uistate"
+	"hearts/util"
 
 	"v.io/v23/context"
 	"v.io/v23/discovery"
@@ -37,7 +39,7 @@
 	discoveryService := ldiscovery.NewWithPlugins([]ldiscovery.Plugin{mdns})
 	gameService := discovery.Service{
 		InstanceName:  "A sample game service",
-		InterfaceName: CroupierInterface,
+		InterfaceName: util.CroupierInterface,
 		Attrs:         map[string]string{"settings_sgname": settingsAddress, "game_start_data": gameStartData},
 		Addrs:         []string{logAddress},
 	}
@@ -54,9 +56,9 @@
 
 // Puts key and value into the syncbase gamelog table
 func AddKeyValue(service syncbase.Service, ctx *context.T, key, value string) bool {
-	app := service.App(AppName)
-	db := app.NoSQLDatabase(DbName, nil)
-	table := db.Table(LogName)
+	app := service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
+	table := db.Table(util.LogName)
 	valueByte := []byte(value)
 	err := table.Put(ctx, key, valueByte)
 	if err != nil {
@@ -69,7 +71,7 @@
 // Creates an app, db, game log table and game settings table in syncbase if they don't already exist
 // Adds appropriate data to settings table
 func CreateTables(u *uistate.UIState) {
-	app := u.Service.App(AppName)
+	app := u.Service.App(util.AppName)
 	if isThere, err := app.Exists(u.Ctx); err != nil {
 		fmt.Println("APP EXISTS ERROR: ", err)
 	} else if !isThere {
@@ -77,7 +79,7 @@
 			fmt.Println("APP ERROR: ", err)
 		}
 	}
-	db := app.NoSQLDatabase(DbName, nil)
+	db := app.NoSQLDatabase(util.DbName, nil)
 	if isThere, err := db.Exists(u.Ctx); err != nil {
 		fmt.Println("DB EXISTS ERROR: ", err)
 	} else if !isThere {
@@ -85,7 +87,7 @@
 			fmt.Println("DB ERROR: ", err)
 		}
 	}
-	logTable := db.Table(LogName)
+	logTable := db.Table(util.LogName)
 	if isThere, err := logTable.Exists(u.Ctx); err != nil {
 		fmt.Println("TABLE EXISTS ERROR: ", err)
 	} else if !isThere {
@@ -93,7 +95,7 @@
 			fmt.Println("TABLE ERROR: ", err)
 		}
 	}
-	settingsTable := db.Table(SettingsName)
+	settingsTable := db.Table(util.SettingsName)
 	if isThere, err := settingsTable.Exists(u.Ctx); err != nil {
 		fmt.Println("TABLE EXISTS ERROR: ", err)
 	} else if !isThere {
@@ -103,16 +105,16 @@
 	}
 	// Add user settings data to represent this player
 	settingsMap := make(map[string]interface{})
-	settingsMap["userID"] = UserID
-	settingsMap["avatar"] = UserAvatar
-	settingsMap["name"] = UserName
-	settingsMap["color"] = UserColor
-	u.UserData[UserID] = settingsMap
+	settingsMap["userID"] = util.UserID
+	settingsMap["avatar"] = util.UserAvatar
+	settingsMap["name"] = util.UserName
+	settingsMap["color"] = util.UserColor
+	u.UserData[util.UserID] = settingsMap
 	value, err := json.Marshal(settingsMap)
 	if err != nil {
 		fmt.Println("WE HAVE A HUGE PROBLEM:", err)
 	}
-	settingsTable.Put(u.Ctx, fmt.Sprintf("users/%d/settings", UserID), value)
+	settingsTable.Put(u.Ctx, fmt.Sprintf("users/%d/settings", util.UserID), value)
 }
 
 // Creates a new gamelog syncgroup
@@ -126,13 +128,13 @@
 	gameMap["type"] = "Hearts"
 	gameMap["playerNumber"] = 0
 	gameMap["gameID"] = gameID
-	gameMap["ownerID"] = UserID
+	gameMap["ownerID"] = util.UserID
 	value, err := json.Marshal(gameMap)
 	if err != nil {
 		fmt.Println("WE HAVE A HUGE PROBLEM:", err)
 	}
 	// Create gamelog syncgroup
-	logSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/gaming-%d", MountPoint, SBName, gameID)
+	logSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/gaming-%d", util.MountPoint, util.SBName, gameID)
 	allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
 	permissions := access.Permissions{
 		"Admin":   allAccess,
@@ -141,9 +143,9 @@
 		"Resolve": allAccess,
 		"Debug":   allAccess,
 	}
-	logPref := wire.TableRow{LogName, fmt.Sprintf("%d", u.GameID)}
+	logPref := wire.TableRow{util.LogName, fmt.Sprintf("%d", u.GameID)}
 	logPrefs := []wire.TableRow{logPref}
-	tables := []string{MountPoint + "/croupier"}
+	tables := []string{util.MountPoint + "/croupier"}
 	logSpec := wire.SyncgroupSpec{
 		Description: "croupier syncgroup",
 		Perms:       permissions,
@@ -152,8 +154,8 @@
 		IsPrivate:   false,
 	}
 	myInfoCreator := wire.SyncgroupMemberInfo{8, true}
-	app := u.Service.App(AppName)
-	db := app.NoSQLDatabase(DbName, nil)
+	app := u.Service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
 	logSG := db.Syncgroup(logSGName)
 	err = logSG.Create(u.Ctx, logSpec, myInfoCreator)
 	if err != nil {
@@ -186,12 +188,12 @@
 		"Resolve": allAccess,
 		"Debug":   allAccess,
 	}
-	tables := []string{MountPoint + "/croupier"}
+	tables := []string{util.MountPoint + "/croupier"}
 	myInfoCreator := wire.SyncgroupMemberInfo{8, true}
-	app := u.Service.App(AppName)
-	db := app.NoSQLDatabase(DbName, nil)
-	settingsSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/discovery-%d", MountPoint, SBName, UserID)
-	settingsPref := wire.TableRow{SettingsName, fmt.Sprintf("users/%d", UserID)}
+	app := u.Service.App(util.AppName)
+	db := app.NoSQLDatabase(util.DbName, nil)
+	settingsSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/discovery-%d", util.MountPoint, util.SBName, util.UserID)
+	settingsPref := wire.TableRow{util.SettingsName, fmt.Sprintf("users/%d", util.UserID)}
 	settingsPrefs := []wire.TableRow{settingsPref}
 	settingsSpec := wire.SyncgroupSpec{
 		Description: "croupier syncgroup",
@@ -225,12 +227,11 @@
 	u.PlayerData = make(map[int]int)
 	u.CurPlayerIndex = -1
 	u.LogSG = logName
+	writeLogAddr(logName, creator)
 	u.CurTable.NewGame()
-	if !creator {
-		tmp := strings.Split(logName, "-")
-		gameID, _ := strconv.Atoi(tmp[len(tmp)-1])
-		u.GameID = gameID
-	}
+	tmp := strings.Split(logName, "-")
+	gameID, _ := strconv.Atoi(tmp[len(tmp)-1])
+	u.GameID = gameID
 	u.GameChan = make(chan bool)
 	go UpdateGame(u.GameChan, u)
 }
@@ -240,3 +241,13 @@
 		ch <- true
 	}
 }
+
+func writeLogAddr(logName string, creator bool) {
+	file, err := os.OpenFile(util.AddrFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
+	if err != nil {
+		fmt.Println("err:", err)
+	}
+	fmt.Fprintln(file, logName)
+	fmt.Fprintln(file, creator)
+	file.Close()
+}
diff --git a/go/src/hearts/sync/watch.go b/go/src/hearts/sync/watch.go
index 877fce5..70fb012 100644
--- a/go/src/hearts/sync/watch.go
+++ b/go/src/hearts/sync/watch.go
@@ -12,22 +12,26 @@
 import (
 	"encoding/json"
 	"fmt"
-	"golang.org/x/mobile/exp/sprite"
-	"hearts/img/direction"
-	"hearts/img/reposition"
-	"hearts/img/uistate"
-	"hearts/img/view"
-	"hearts/logic/card"
 	"os"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"golang.org/x/mobile/exp/sprite"
+
+	"hearts/img/direction"
+	"hearts/img/reposition"
+	"hearts/img/uistate"
+	"hearts/img/view"
+	"hearts/logic/card"
+	"hearts/util"
+
 	"v.io/v23/syncbase/nosql"
 )
 
 func UpdateSettings(u *uistate.UIState) {
-	scanner := ScanData(SettingsName, "users", u)
+	scanner := ScanData(util.SettingsName, "users", u)
 	for {
 		if updateExists := scanner.Advance(); updateExists {
 			key := scanner.Key()
@@ -40,7 +44,7 @@
 			break
 		}
 	}
-	stream, err := WatchData(SettingsName, "users", u)
+	stream, err := WatchData(util.SettingsName, "users", u)
 	if err != nil {
 		fmt.Println("WatchData error:", err)
 	} else {
@@ -87,7 +91,7 @@
 	}
 	fmt.Fprintf(file, fmt.Sprintf("\n***NEW GAME: %d\n", u.GameID))
 	defer file.Close()
-	scanner := ScanData(LogName, fmt.Sprintf("%d", u.GameID), u)
+	scanner := ScanData(util.LogName, fmt.Sprintf("%d", u.GameID), u)
 	m := make(map[string][]byte)
 	keys := make([]string, 0)
 	for scanner.Advance() {
@@ -112,7 +116,7 @@
 			handleGameUpdate(file, key, value, u)
 		}
 	}
-	stream, err2 := WatchData(LogName, fmt.Sprintf("%d", u.GameID), u)
+	stream, err2 := WatchData(util.LogName, fmt.Sprintf("%d", u.GameID), u)
 	fmt.Println("STARTING WATCH FOR GAME", u.GameID)
 	if err2 != nil {
 		fmt.Println("WatchData error:", err2)
@@ -196,7 +200,7 @@
 		u.PlayerData[playerNum] = userID
 		u.CurTable.GetPlayers()[playerNum].SetDoneScoring(true)
 	}
-	if playerNum == u.CurPlayerIndex && userID != UserID {
+	if playerNum == u.CurPlayerIndex && userID != util.UserID {
 		u.CurPlayerIndex = -1
 	}
 	if u.CurView == uistate.Arrange {
diff --git a/go/src/hearts/touchhandler/touchhandler.go b/go/src/hearts/touchhandler/touchhandler.go
index a76f12d..6d1cbc4 100644
--- a/go/src/hearts/touchhandler/touchhandler.go
+++ b/go/src/hearts/touchhandler/touchhandler.go
@@ -163,8 +163,11 @@
 		} else {
 			for _, b := range u.Buttons {
 				if button == b {
-					logAddr := b.GetInfo()
-					success := sync.JoinLogSyncgroup(logAddr, u)
+					s := strings.Split(b.GetInfo(), "|")
+					logAddr := s[0]
+					creator, _ := strconv.ParseBool(s[1])
+					fmt.Println("TRYING TO JOIN:", logAddr)
+					success := sync.JoinLogSyncgroup(logAddr, creator, u)
 					if success {
 						sgName := sync.CreateSettingsSyncgroup(u)
 						if sgName != "" {
diff --git a/go/src/hearts/sync/util.go b/go/src/hearts/util/util.go
similarity index 77%
rename from go/src/hearts/sync/util.go
rename to go/src/hearts/util/util.go
index 7155506..aad0831 100644
--- a/go/src/hearts/sync/util.go
+++ b/go/src/hearts/util/util.go
@@ -4,13 +4,13 @@
 
 // util.go stores constants relevant to the syncbase hierarchy
 
-package sync
+package util
 
 const (
 	// switch back to my mountpoint with the following code:
 	MountPoint = "users/emshack@google.com"
 	//MountPoint        = "/192.168.86.254:8101"
-	UserID            = 111
+	UserID            = 1112
 	UserColor         = 16777215
 	UserAvatar        = "woman.png"
 	UserName          = "Bob"
@@ -20,4 +20,7 @@
 	LogName           = "games"
 	SettingsName      = "table_settings"
 	CroupierInterface = "CroupierSettingsAndGame"
+	// Swap the following two lines when running app on a computer vs. mobile device:
+	// AddrFile = "src/dataParser/addr"
+	AddrFile = "/sdcard/addr.txt"
 )