diff --git a/android/build.gradle b/android/build.gradle index 2d80128..af6c1fd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,7 +35,7 @@ android { } dependencies { - //implementation 'com.google.android.gms:play-services-auth:18.0.0' + implementation 'com.google.android.gms:play-services-auth:18.0.0' implementation "androidx.core:core:1.2.0" implementation "androidx.browser:browser:1.2.0" implementation "androidx.security:security-crypto:1.0.0-rc01" diff --git a/android/src/main/java/com/tailscale/ipn/Peer.java b/android/src/main/java/com/tailscale/ipn/Peer.java index 6b735e8..654b819 100644 --- a/android/src/main/java/com/tailscale/ipn/Peer.java +++ b/android/src/main/java/com/tailscale/ipn/Peer.java @@ -19,24 +19,23 @@ import android.webkit.WebViewClient; import androidx.browser.customtabs.CustomTabsIntent; -/*import com.google.android.gms.auth.api.signin.GoogleSignIn; +import com.google.android.gms.auth.api.signin.GoogleSignIn; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; import com.google.android.gms.auth.api.signin.GoogleSignInClient; -import com.google.android.gms.auth.api.signin.GoogleSignInOptions;*/ +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; public class Peer extends Fragment { - //private final static int REQUEST_SIGNIN = 1001; + private final static int REQUEST_SIGNIN = 1001; private final static int REQUEST_PREPARE_VPN = 1002; @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { - /*case REQUEST_SIGNIN: + case REQUEST_SIGNIN: if (resultCode == Activity.RESULT_OK) { GoogleSignInAccount acc = GoogleSignIn.getLastSignedInAccount(getActivity()); - android.util.Log.i("gio", "Account: " + acc.getId()); - onSignin(); + onSignin(acc.getIdToken()); return; - }*/ + } case REQUEST_PREPARE_VPN: if (resultCode == Activity.RESULT_OK) { onVPNPrepared(); @@ -56,13 +55,15 @@ public class Peer extends Fragment { super.onDestroy(); } - /*public void googleSignIn() { + public void googleSignIn(String serverOAuthID) { GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken(serverOAuthID) + .requestEmail() .build(); GoogleSignInClient client = GoogleSignIn.getClient(getActivity(), gso); Intent signInIntent = client.getSignInIntent(); startActivityForResult(signInIntent, REQUEST_SIGNIN); - }*/ + } public void prepareVPN() { Intent intent = VpnService.prepare(getActivity()); @@ -83,6 +84,6 @@ public class Peer extends Fragment { private native void fragmentCreated(); private native void fragmentDestroyed(); - private native void onSignin(); + private native void onSignin(String idToken); private native void onVPNPrepared(); } diff --git a/cmd/tailscale/callbacks.go b/cmd/tailscale/callbacks.go index 99d2402..f784bbb 100644 --- a/cmd/tailscale/callbacks.go +++ b/cmd/tailscale/callbacks.go @@ -33,8 +33,10 @@ var ( // instances being created. onPeerCreated = make(chan jni.Object) // onPeerDestroyed receives new global instances of Java Peer - // instances about to be destroyed + // instances about to be destroyed. onPeerDestroyed = make(chan jni.Object) + // onGoogleToken receives google ID tokens. + onGoogleToken = make(chan string) ) var ( @@ -66,8 +68,10 @@ func Java_com_tailscale_ipn_Peer_onVPNPrepared(env *C.JNIEnv, this C.jobject) { } //export Java_com_tailscale_ipn_Peer_onSignin -func Java_com_tailscale_ipn_Peer_onSignin(env *C.JNIEnv, this C.jobject) { - // TODO(eliasnaur) +func Java_com_tailscale_ipn_Peer_onSignin(env *C.JNIEnv, this C.jobject, idToken C.jstring) { + jenv := jni.EnvFor(uintptr(unsafe.Pointer(env))) + tok := jni.GoString(jenv, jni.String(idToken)) + onGoogleToken <- tok } //export Java_com_tailscale_ipn_IPNService_connect diff --git a/cmd/tailscale/google.png b/cmd/tailscale/google.png new file mode 100644 index 0000000..a9de963 Binary files /dev/null and b/cmd/tailscale/google.png differ diff --git a/cmd/tailscale/logo.go b/cmd/tailscale/logo.go index af01750..f16c3ad 100644 --- a/cmd/tailscale/logo.go +++ b/cmd/tailscale/logo.go @@ -1,6 +1,7 @@ // Code generated for package main by go-bindata DO NOT EDIT. (@generated) // sources: // tailscale.png +// google.png package main import ( @@ -65,7 +66,24 @@ func tailscalePng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "tailscale.png", size: 24866, mode: os.FileMode(436), modTime: time.Unix(1589933683, 0)} + info := bindataFileInfo{name: "tailscale.png", size: 24866, mode: os.FileMode(436), modTime: time.Unix(1590514565, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _googlePng = []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x00\x00\x00\x01\x06\b\x06\x00\x00\x00\x8a+K{\x00\x00\x1b(zTXtRaw profile type exif\x00\x00x\u06ad\x9bir\x1c\xb9v\x85\xffc\x15^\x02\xc6\v`9\x18#\xbc\x03/\xdf\xdf\xc9*J\x14%u\xf3\x85-6Yd\r\x99\xc0\x1d\xce\x00\xa0\xdd\xf9\x9f\xff\xbe\xee\xbf\xf8g\xad6\x97Km\xd6\xcd<\xffr\xcf=\x0e~i\xfe\xf5\xaf??\x83\xcf\xcf\u03cf\u007f\xe1\xfd\xf3\x97\u7777\xf7\xaf\x91\xc7\xc4cz\xbdP\xc7\xfbS\x83\xe7\xcb\xcf\x0f|\xdc#\xcc_\x9fw\xed\xfdJl\xef\v}\xdc\xf9}\xc1\xa4;G~\u065f\a\xc9\xf3\xf1\xf5|\xc8\xef\v\xf5\xf3\xfa\xc5z\xab\x9f\x87:\xe3\xebq\xbd\xdf\xf8\f\xe5\xfd\x1d\xa7\x9fO(\xde\x17\xd7\xdf\xee\xf3\x13\xb9\x12\xa5]\xb8Q\x8a\xf1\xa4\x90\xf2\xd7/\xf7O/\xfe'_\xee\u07a5\x10\x05\xdf\xdeq\x1aJpT]3\feN?y\x17\t\t\xf7\x9d\xb7\xf2\x04\xf8\xe3\xeb\x9d~\xff\xa9~(U2X\x9e07&8\xfc|]b\x96\xf0\xb3\xb6\u0493\xe7\xc4\xfb\n\x8f\xaf\x16\n\xae\xee\xf7\x05\b\x11\xf7.\f&$2\xe0-\xa4\x12,\xf8\x1ac\r\x8186\x124\x18yL9N2\x10J\x89\x9bA\u019c\x92EWc\x8b\xba7\x9f\xa9\xe1yo,\u0462\x9e\x06\x9bHD\xa1\xb3*\xb9\xe9i\x90\xac\x9c\v\xf5Ss\xa3\x86FI%\x97R\xac\xd4\xd2\\\xe9eX\xb2l\xc5\u032a\t\xe4FM5\xd7R\xad\xd6\xdaj\xaf\xa3\xa5\x96[i\x02\xc2\xd6z\x1b=\xf6\x04\x06\x96n\xbd\xf6\xd6{\x1f#\xba\xc1\x8d\x06\xd7\x1a\xbc\u007f\xf0\u030c3\xcd<\u02f4Yg\x9b}\x8eE\xf9\xac\xbc\u02b2UW[}\x8d\x1dw\xda\xc0\u0136]w\xdb}\x8f\x13\xdc\x01)N>\xe5\u0629\xa7\x9d~\u01a5\xd6n\xba\xf9\x96k\xb7\xdev\xfb\x1d?\xb2\x16\xdem\xfb\xf5\xeb?\xc8Zxg->\x99\xd2\xfb\ua3ec\xf1\xac\xab\xf5\xe3\x12ApR\x9432\x16s \xe3U\x19\xa0\xa0\xa3r\xe6[\xc89*s\u0299\uf466(\x91A\x16\xe5\xc6\xed\xa0\x8c\x91\xc2|B,7\xfc\xc8\xdd\xcf\xcc}+o\xae\xb4o\xe5-\xfe[\xe6\x9cR\xf7\xff\x919G\xea~\xcf\xdb\x1f\xb2\xb6\x85\xcd\xeb\xc9\u062b\v\x15S\x9f\xe8>^?m\xb8\u0606Hm\xfc\xed\xf1\xb4\x9b\xdb\xf5vF\x18\xedl89\\\xb3\xe6o\xea\xf5\u0395\xeb\xf4\xe3\xec\x9b\u077e~\x1aQ\xaaq\x842+\xb05H9\xd7\u06310NB\u007fzY\xbb$@\x9c\xf0\u0359v\x01\xc7Jhm\xdcsSYc.j\xc45.\xbd\xd3]i\x0e\x82Tf\xdbs\x8f\xe9K9c=\x17l\xeb\x86U\xd3-\f\xee\xac{\xc2!H\f\x8f\xf93\xc5\xe3\x0f\x11\xe2eGM\xf5q\xae1\x89\u0521\u00d1\xf6\xcci\xefA\r\x9c0\xfa]\xb5\xf6\xc4K\xb3^_\xacu\x1f\xbb\xdf\xc6X\x8c(v\ua987\xdb\xe9\xfeYF\u0611\xd0\xceZy\x9eb\x9b\xd6\xf6\xae\u742b<|\xe3J\x94\x1c\xe1\\\x11\u0728q\xef~\x98\xc3\x02d\x06\xd7\xce!\xde\x0e\xfa k\x98\xbc\x9d\xc0cF\\\x14\xca#\u05dd\xc0\x92Uty\u0296\xae$\xf5qR8\xb7.\xaao\xaf\xda\xe2\u0339\xef\x92\xe3\xf2\u05df4\u076aL\xb4\u07b4v\xa5lK\x19\xa3'b\xb1N\xed'X+\xf9\xb6\xe3\x99 ?\xed&Eh\xb4V\xc6:7\x98X\xad\xef\x89N\xd8\u02c9\xbbmvR\xb5iM\xaa,E;F\xa1\x81\xcb\xcbn9\xa5\x0eS\xa1^\x94\x03\xd5\x0f\x88\xc5A\xb0{n4\x1c\xcd\x12HB\xb3\xe4\xe2\x88\xfb\x16\x064\xee\x13\xdda3s7\n\x91\xbf:zn\x8c3\xe6\ta\xa7\xca\xdb\b\\\xbdFS(\xa3\xdc)\x05\u02958\x12\xec\xc6\xed\xeb\x8a}\xd05\x96g\xe0\xd9\xdbr\xbdkrOc\u04a1\xec\x9e/lJ\x90gJ\xcb/\x86YZ[\xe9L\x06\xdfn\xcd\x16\xb6S\x94\xcb\xf6\xdc\xe3#\xde\xdfy,S\x05E-\xefu&|\x91\xb7\x8b\xb1\x13\xdb\n\xff\xc6Zi\xc9B\xff\xaczf\xa1vh\xfa\xb9\u06a6\x1c\xfc\xe8\xf4\xe2\b\xea\xd2N\x13\xdb\xe9\xa9\x12\u0632(\xf7m1n\xb7n\xa4WiTZ\xb9\x8et\x1a\xf1H\x93\xd2:\t\x89[\xb8L\x9dpf$E\a`<\xb4@\xb1I57\xc0\xa1O[\u04ee\xf5\x80\x18E\xe2\xd1\x15\xf5\x8c:w\xe9T>3\x8fV\xd2&\xf9\x8d\xbe_\u0798\xd2\xf4\x87\xd4\xd6s\xf3\xa4\xc0\x0f\rO\\^\xbd},\xed\xe6\xddx\xfeB\x17}\xf7\x110\xa7~\xdaz\x03\xc4)\x97\x80ys\xc23?\xac\xcc\x04\n\xdc\x1av>\xbe\x82[\xb7\x95\xda\xf7VK.\x06 \xa54G\xeb#\xd5p\x00\xc2;UYk\u043d\xd4\x11\xf3wc\xd1\xf1\x8c\xef\xa4\x05\\\x10\xf6\x92\x1a\x13;\u02e6\xda=\x8e\xe2\x01\x941\x04\x90\xf5\xe4\x9b\xe9\xc1\x9a\xc0S\xcd\xd1j\u06e4\x90\v/w\x04\x02\x14\xe5\xe9-\xd2\x05\xb4\x04\xcaj@5G\xd9\u06f9\x18\xd8-=\b\x0fL\x92\x91\xc1\x9a\xd4\xf9\x0f\fWL_s\x06\xb3\xff^&\xe0\"\xcd5C\x05\xc3\U000e6091^\x14(\x83\xe8\v\x18\xa1\xb2\rBYT\x85\x9f\x0f\xf8S$\x89I\xf3\xf1\xd4\"7I\xb5\x15j\xfe\\`\x84Q2A\xb0\xa0\xa1\x0e\xd5`\xb4&H^\xd2mz\u031b\xa4g:\r\x11\xc1/\x05\xae\x82\xe6JQ7\x87\x03\xf5\xfcx\xf9O\xaf\x02\x1e'm\x03\x00\u019cT\u051d}\xf9\x83\xce\x06.b\xbf\x910\x935{\xcd\x10\x06\x84\x90\x9f\xdf\xfd\xf7\x1e\u075f^\x88\x02p\x18y\n\xbb\vq^\xd4\x04\xc1\x03b\x88\xabY\f%\xfb\x05\xcc\x12'\x8ad\xd66\x0060h@\x0e\xf0m\xa7\xd1'\x04}\xf1+\xf7\x8ao\t\x15P\\`\xcf\x125\xca\x00w\x87\xc9\xe7\tV\x87)n\x06-\xa9\x81Y\x97\xcb\x14\x19A\xa5o(/\xcag\x86t\xa0\x86\\\x81=T\x9e\x9a\vr0\xe5\x98Fj9dd5\b\xd4le\x9a\x15\x02\x87\uee17\xdbt\u07d3q\x8d\xf8K%\x94\xb3c\u036b\u04e6\x9b\xcfO@(\x88\x93w\x03\xf5\u01ed\xa9\x9c9\x8da\xfa\x03e\u04d2{l\xca8&\x80\x8cb\x9dwn\xa6\xd02\xf0\x1eF\xb1\x8e2\xe9\xd9\xdfI\xcdxt'\x83\x85\xb3\xa8\xceH\x87\u00e8e\xf6S\xe8\xb5\x19\u04c15\x1al@^{\x15\rL\bj\x85j\xaf\xb2\x9d\x00\xab\xfd\x89\xce\a\xfaa\xc0\x04 \xa7wtf\x05\xd4V\x16\x1d\xd4\xce-\xa8\x8a\xb0\xb9\x9a'x\x95\xf9\x9f\x97\xb8\xc1fFx:,/\u0727\xc7/\x93\xa7\xf5\xaej|\x9b\x1b\xdb:\xf5\xbf\xa1\x9d|\xe02\x94I\xb3P@\x9ep&\x9a\xe1\xc0\x9b@\x1b9\x85\x16}\xe7\x89C\xdd\xc5\x02\x06\u8d98\xd8s\xc4B\x0e\x05\xc0m`\x03;\x05MT;Z\xcb\xd7f\x06\xa9 C\xa0\xbcA\xa9\x90\u0378\xba\xca=\xc3\x10 \"A\x9d\xa9\xf3\xd4m\xc4e\xce\xe1\xf0>-\xde\x06F\x9fj\xf3\xd4-\x9d\xb4AY\xf0\x86\x11m\xb0\xf4\"\x86\x90\u007f\xbd4h\xf6\x1a\x04\x99/H\x84Uk\"\xf4\x98n'\xd8\x15\xab\xd6\xc0\x9e\xcc\xc5\xedFP\x93\x1b\xd2\xfb\x85\xa8@\xf9\xc42!\xb8\x00\x17f\xd2,\n\x90\u1aba'\xef\xad#\xde\u0419XX\u0361\x92 r\x1a\xe3d\f\x83\u0346\x80\xc1/\x19`]^C\xa1\xe6\xf3\x03\xf1\xe8\x9b[K\xf1k\xe4\x8bt\x01\xaf\xc6D\xacFn\x19\xbb\xa3jb\xf0kIHm\x80\xf0N\xf2\xd0;B\xcd\xd2Qa\x9c\xaa\xca\xcc\xf4\xc8L\x86(\u0186\x9eyAI\x05/KfnS\x8b4\xca\xd7\x0e\xec\xd63\xeaY\xe5t\x86\x0e)\x8d\xf4\xb4\x95w^H\t)~\xcc\xe7\xab\b\xc1\xfe/\xa2\xd0\xfdx\x82\xb0\xc0@ \xc560\rY\x06r\xe0\x94\xc0\u0158\xbb\x1fZ\xa7\x12\x91\xc1 \x90\x10.\x00E\x82,\x85?i\x054\x91C\xba-C\xbc\xf9)%\x99(\x92(\xd4I\x81\xd0a3w]`1~>q\xddx\xb7\x90)\x82\xe2uh\x12^2\x06%\r\xb58\x84\xc5\x04\x1bS\xf4b\xe5\x95T\xf5\xb0*6\x10#3\xa7\x16\x97+\xd8B_/\xe2\xe5\xf1M\x97\xf7\u0447p]_\xb4\x00 \u007f-DwV'\x12\xa0=\xba\xd9\xef\x02Vg\xcaFJ\xe5\xa0l\x02\x9d\xf4\xe84:M\x83h3\xee\xd6Sd:\x9d\xfb\x15\xec!\x1e\x15\xd0].P\xaea\xec#\xc1\xda+\xe5D\x1f\xe0\xee(\x8e\x03\x99e^-\x02\xbd\r\x93A\xeaXgF\x11\x1e\u020aZ\f\xa1\x86\x10\u0170\x823\xc6o\t\x80\xa1\xf9\xf0\xbb\r\xdb\x00\xb23<\xc2\x05_Q\xad\xcc\n\xa6M\x8f\x90K|\x9e\xc0\u0277\xc9_\xe2\x0fi\xa9\x8d\x19)\xa8\x91)\xf8\x80\xb2V\x8c\x81\x14\x85B\u052d\x9c\x8d|-u\x98d\x80^\x95`\xf9\xe9L\xf7m\x91\xf8/\x8f\xbaP\xb8\x80\u050a\xb4m\x91\xacC.\u0474\x1d\x89\x84DF\x05B\xbe\xb09\xb0*\u03f8\u01e1\xeb\x10=\xa9\xfa\x99\xc5\x06[\xeb:\xe0\x11\xda\x1b\u0740\xeb\xcaD\rF6\xc0\x87\xa2\x01\x18\x98\bB\xf2Y\n \x01\xe4\x1a\xcbj\x97K\xf3\x01\xc0\f+ \x9b\xfc\xa8\xbd\xb5\xcca\x1c[\xf6Q\xab\x82\xe4\x13\u007f@\xbf\"\x82\x90*\x01\xac\xa3\xf3Px\x10y\xd1\xea\x16,\x05z\xca\xee\x10dYA\x1a\x0e\xbe*\x80\xdctd\x91\x9a\x8fYN: \x0e\xe90\xea\"\u0503m\xb8\u032a\x02'%\xca7xD\xe8 \x1f\x04\x9a\xba\x04\x02 \xdc\x16\xa5`\x00\xae\xe9\x90\xeb\x88\x17\x11\x12\x14\x84@\xa5\xf6\xe0\xd8J\xdf7\x00\x8a\x9cu\x89\x8e\x8ff\xe5\x16\t\xd4\xfa\xcd\xc4\xd5\xe8\"\xea\t\xf1B\xcfw\x82\xea\xe3h\xf0\"\x1e\x13y}^\x1c\u07f17\x8c\x8e\xdfp\xf4\x8fM\xa7I\xd0t\x19g\xb6%\xdd\x0e\xc2\xf7:\u051e\x9a\xeb\xe0\xf5\xd0= !V\xf4\x1e\b\x14\x8c,\x81.-\xe3\xf90\u00aeP\xa2\x9b\xf8\u07d76\x96\xf0>|\x10%u\xd3u\xe0Y\uf5cc\xa3(\nM\x8b\xae\x81\xbf@\xec\x8e\x19\x1f\xba\x04\x92j\u04cb\xdc\xc6hE\xaa\x00\x90\xaeE\xaf\x10\xd0)s\x80\xc7Dh\xf1I\x1a\x16\x94\xa4ty/\x82\xf5v1\u043f\r\xf2\xd7!z\xf7\x1ee\xfb1\xc6C\x81\xfea\x80\xc4\xea5\xc4\xdf\x06\u8f46\xe8\x18\xe3\xe7\x11~\x1a\xa0v\x10\xbe?D\xf7\xb7@\xfe\xeb\x10\xbf\xc4\xd0\xfd[\x10\xbf\x1bC\xf7oA\xfc\xee\x00\xdd\xd7 \x827s\u07e1\x15\u0192fB\x86\x1c\x99`E\xaf\u00aaIR\x0e\xe5\xfe,r\xeej\xcc\x0e,\u007f\x1c\x11\xf0\x05\x96\xeczZ\xed\x1e}\x14\x97\x97|%Pk\xa3\x99O*\br\xb2;\xc7\v\x01\v\x9d\xbe~_\xa5\xfa\u068b\xee\xb7f\xfc\u066f\x1f\xdd\xfaz\xf9\x8aj\xf9\xfc\x0e\x17;\x86\xf9A]&\x1a\x8c\x01M{\xafC^\x93l\xf9\u03e1\xbax\x9cW\x86iK\x0f\xee`\x8b\xf2\x9dr\x89\xb1hg\"z\xb8\xf91\x8c\x10\xcd*\xc1\u0648\xe8\xd6Zr\x83\xff\xb0\x9c@\xed\xc2r\xc3\xde\x145\xd4\xdf\xe0\x19<<\n\x83\x0e\xa5'OJ\xb3\x85\x88\x1e\xdeG\x16\x18\x87S\xae\xae\x83x|\x96Q\x12\xee\x83\x1cP\x01\x0eX\u8228\xf2\xacn?\xac9\xec5\x19\xfc#\x9f\xf0\xf3Y\xfc\xc1\xd8\xf4\x015\xd9Y\r\xbf\x95\xc5\xdc\xd8\x12\xe9\xc3\xd5\u01f8\r:\xa2\xc9\re%\xb1\x88h\xa1Y\xc7\xd1J\x02,\xbe\xbc8\x90V^4\xf2\xe5\u00e3R\xd8H\xf4\x9c\fK\xaa\x05\x1f\xd4 \xfc\xca|\x1dU\xd5\xe7\x83E\be\xdc13\xa4\xfb7\xef\x87\xc6\x17rK)\xaa\xc8%0\xa0\\n\xe7a\xef\t\x1a@\xe3*\xea#\x13\x8d#q \xcex\xb6&\x96\xe4:\x13\xaf\a\x81\x99\x92\xbd\x98\xb5b.~\xb7g\xa4!=\x94\xc9\x1c4\x0e\xb1H\xb9\xe9\xe1\x88\u04d0#Zn\xa0O\xe0\xf7\x01~\x98JM\v\x15^[!E\xb2'\x9e\x05\xad\u0594\xd1\xc1[[\x17\x82\xf8\xb3\xc3<\xeeE4\x8fs\xc6\u0343\x91I \xde\xf6\xa5\xc2y\xcb\x12\xe8\xee\at\x03\xe3\xabt%\xed\x05E\xb7\x8e\u07bc\x86\xfd\u05e2\xefF\xd6\xfc\xbc\xa8\x98\u1e62\x98\xe1\x1f\xae\x19D\b\x1f\xd7k\ufaf9\xe7r\xbf\\\xed\xfb#\xfc|=\xf7yx\xff\x97\u0479\xcf\xc3{_\rz\x93h\x9aO\x9d\x96\xb5WB\xe9D)\x9e\xb6\u03a5>e3\xe8\x15\xe4\xfd\xc4_\u07c2\xcd\xc3Anl\xba\xf6\x80m\xf29\x04\u075d\xa9S#\xd4\xc7{\x85\xad\"H\u007f(\xaa\x13\x1f8AB\xa6\xd2)\x1ct\xe1\xd0^>,\xe2!\u0300\xf9\x8d\x81\x9a\xa5\xaf@\x88\x8d\x88\xafLs\xcf3\xb9\x01\x84*\xdf\x12\xb4\xff\xd4ae\x8a\x02\x95\x8d\x1c\x9bK\x16\x96\x8e\x1b!\xb8v\xb5\xc0\xb80+\x81\xeb!\xf5nG]\xde\\\u0407\xf0d\\a\f@\xbd\u70d5\xa0W\xbd\xecMX\\\xf0\xe0\xf07\x83C\x85\xf6\x81\x85\xe0\x87\xd6\f\xf9\xd4P\xcf\x1d\xc46\xf1-\x16q\x89\x006\xba[k\x87\xa3j\u02db~\xbb\x03z\u07d6\xdb.\u03d1\x82$\x12@\x06;^\xc7\u007fB\x88\xa9i%\x1c\xf2{\x1awk)>\xc8cv.\x84\x81\u008b\u06a8k\xa2s\x1fWRM\xf2[\xeb\xcc\x04\x10\xf6r\xe0\u00a4\xbb\x80\xa7\x10\xdf\x1b)u\xf5g#E-\x8f\x11\xe2\xf2^>t\xce\x18\xd7\xde\xfb\xd0\xfb%\x10\x02\td\xd4\xca\u043a\xc0xZ\x04\x87=\xb4\xb7i\x00J\"T\xc8\x17|\xd6E\x14\x84\xb8\x17\x00\x95\xbb\xec>*D\xfe\x86\xb2\xd2^\u0263m\xc0\xb22\x16\xc4l\x0e\xae8ZJZZ\xd8A\x8fh\x0f\x19\xd8C\xae\n\xe5\xc19@\xe7aY\xbb3\xef\xa6}J<\xab\x15\x9a\xb9\xe3SzD\xe3\xb74\x83\xbb\xa2\x1c\x8c\xb8\xf6\xce\xfc\x19\x04\xcer \x8ah\x87\x1c\xf3\x01p\xc8\xeb \xb45s\x11j\x01S\x0e5\xe0,\x9a`\t\xe9\u01339\x9b\xe3\xbeMngS[}\xe1\xf3\xb0\x88\xd8Q\xd3\xe6!\x1f\xc6Pk\u28c7\x12\xb5\x11\xf8\xf6h\xa5}\xe5o\xf7O\x04O~\xb8\xfd\x0eA[\x81\xa53\xba\x8c!\xb32\u1e05%\x06g\x9b\u00a9\xfd.\x17;\x02\xb2Q\x9a8\xe9\x02\xc0\x0fB\x1cQ\x02\xf3\xb1b\xeb\xf44\xa8p\xaa\x8e\xf9\xf5\xded\x8au\x9e\x06>\xc1\xcc\x00\xc1\x1f(\xecfz\xe9\x91.-@\xcdj%=\"\xb9\xf0)U\x1b\xe8h\xb7\x17\u07d5\x9f\x8b%\x14\u047c\xf06\xc5\xec!\x85\xa5Fv\x140TT\a.\u06e7[\b2\xb6\xf5\xf4}\x83?\xd8q\xaf\x037\xd3p\xed\x88\u078cP\x82[\xaa'K\x01\xa1\to\x14j\x06\xb4N\u076d\xa2\x92]\x03\u0710Y\x9f\x90d\x03\x03P\x98\xb2a\\\x84f\x85\xe4\xb5m\u06b2\x85\xd6\xf7\x02\x1c\xa6hhi{E\x88\x82\u058a\x8eF\u013aum\xec\x80\x06\x83\xfb6\xac\xbf\xa1\xdab\xb7\xc9\xcd):\uef35\xb4]\xf0\xf3\u0424\xd7Z\rE\x01\xd9\xee4\u01f3\u0324\x05\xcd\xf8\b\xbd\xa4~d\u0610<\xc5\u0280+\x9d@l\x0fu\xf5\xd2\xdb\xcb\xc7g\xdd`\xbd\x85\x18mdT\xdek\r5\xb8\xeeM+\xd2\xc9ofTuT!n`\x95\x06L)H\xa3UE~hi3\xe7\xb7$\xd2\xe2\u04cf-\x8f\u0100\xf2\x9a\xce6\xcd\u04b5\x82\xdcuVI\xd2rh\x19\xa7h\xa9vi\xf7\r\xbb\x8cvk [\x92\xd8\x1fP*#\xb8E\x8dN\xa5\x0f\xad\x99\xf4\u0385\xb4\xea\x00(n\f<\x1a\x9f\x8a\xda\xe8$Jk\x8b-\xa9\x95\x83\a\xe0\"\xc1h\x81\x89]\x06\r\x00`\x92u\xae\xd7\x01\b@\x89\x96w\x8bp\xea\x94\xd5A\x86t\x94#\xa0G\xae\x04OcQ\x14'\xd3#\xea\x14\xb4\xb1v:H\x8b\xda\xef\xa3S~6\x82\xfbCgh]\xfb\xd9MD\xf3S\xc00B\xce\x19u\x91p\x1aw\x8e\x966\x06#\u043a`\x05|\x01$\u0416\x0e\xcb`\x89\x94\x015\xe4\xb7\u036dC\x1e\x14`OW[\xdfY\x1b 67I\x82ml\x03\xb6\x94\x1a!\xdd\x1b\xfc\xc5\xdek\xcb#\xa6\xbd\\\xe7\x0e\x88n\x10l\xe6\xa4\xe5\xff\xecSB\xf8\xf1,\x8a\x91y0H\x1cGj*\x06@\x13y\x87)\x02\x86%O\x11as6\x82L\x91\x86\xed!\u02ec\xe5C\xdc\\\xd0\xd6\xf0\xa2f\xa82\x19\x04.\xbc;\xfa\xf2`E\xe9\xf2G\xebj;\xec\xab\x1eO\u0460z--\x8d\x88\x18\xa5\xde\xdan\x92\x00+I\xe9\x15|\xc8c\xc3q3 \x1b)$\xb4\xb5\u06e3\x19\u04f2\x12io\x13F\xb4\x87j\x1a\xea\x1bptE\xa6\xa9i\x0f\x9c\n\xd9y\xe4\xf3\xecu\xf39|\x15}g\xb4/\x9e\xb2\xcc\xdc\xfd\xac`5\xf3\x85\u0098\xb2\x8c4\x15v`\x02\xee\xe8H\x17n\x99z\u0242-\xb4\x06\xa2\x83\x06\xa7\x1c7\xf6p\u00e8EK\xca\x11\x93\xc5Pso\x98`\xe2\x06\x80d\u007f\u05f3X\v\x8d\"F\xf1\x17\x8cC\xab\xe2\x00\xac\xb6\u00f9\x17T\x15a\xcf\xf9l\xa2\x02L\xdcfCHh\x8b\xa5]\xa8\xa2\xed\xb9\t0o\x1dx\xd2\u0236\xbd(\xfb\xa6+\xe4\xd6\x1a\xc4\xc6$K\x1f\xe2UP\x12^EM,\x18\xb5\f\xa5v\x10\xb8\xc5@\x18\x12\x99\xa2\xf3E\x8b4\xcb\xf7#k\x9e \xae\f7\xa1\x12=\x1f,~=\xac\xb4L\xc7C\xd2M\xdaf\x04m\xb3\xf6C\xca\xceA\u05cd\x1088\f\xdf!\xf9(\xa7\x02\x8b4\xad'\xc3V-\xb4\u01506\x1d\xb7\xea\x14=\x8a\xa04z\u0732\xban;`\xed\xaa\xf9H@.\x11\u05ca\xc1\xd7~\xbc\xe4M\x00F\x175\xa2\x88Q9 u\x9a\x1d\rB\x01\x10\x1cX\bw\xbd\xa07\xc4\xc1r\x153\u0601\xef\xaeM\x03j6\xc2\x19\xa8'\u0504\xbdj\xfc\xbd\xb8\xb3\xae4\vrK\r\x8c\xa1\x19\x99\xa6\xc7\xc0S\x81t\xa7\xd1\xfd|\x8a+\x96\x85\xb6\xeb\xda7\x968\x9a\xe19\x94\xe8{\xc5\xf4\xbf\x00\x892\x1a\xff\xb4\x82\xe0\xbes\x06*B4HZ\xf4,\xe0\x8e\f\x01\xb4\xf7\x8a[{[c<\xe3=\xe6\u043fek\xfb\x02\xe6\xdb\x173K\b\xb1\xfch5\x12\x1d\x906D\xd3k\xeb\x95\x06\u0086'\\\xd3\x15\xbd\x91\x9f\xaec.#\x87\u7333\x8b\x05\x19\xad\xb9A\xf1\x84\n\xa9\xbf\x97\x84\x9d.E2\xa2\x16\bG\x9b\x89$c\xfa\x0e\xa0~\xf1\xf5:_\x80J\x1d\xb5R\xc8\xe0_\xbd\xaew)\r\x9d\x19\x04\x11\xa0\"d\x17\xdaG\v\xf5=\U000ce92d/^\x19b\xcc\b\x946\xca\xf3I\xd5x\x89\xcf8_G5\xdd\x1aE\x1ac\xe7.\xbd\xf9^\u0588\xe9\xf7Etz\x84\xc2\xce:\xe3\x15(\x0f\x1de\xc4K\xa0\x94UZ\b\xf6\a\xe6\u04d7*\xc2\x03bT\x9e\x03G\x9b\x16\xec\u06c0\xee\x02\x86\xa7H\u0799\x045\x13{\u042e\x05\xb0:\xe8\xaa\xe0\xe8\u012a\xa3L\x95<,\x84\x13\x82\xc2\x14\xc1#O\x84\n%x\x10F\xd0q\x8d\x8d@\\\xda\xfc\xcctw\xaa^k\xf5\xa8\"-z\\\x04\xbb\xf6^\x1fb\xa6l\xe9B\x8b\xe0M|\u0387%<#@W\"q \xe0:#\b\xcdR\xd7a\xa9\x1e\xe0:p\xe7[\x16\xe2\xeb#`\xe4\xa9\xe4*\xe1\xaf5\x92\u044d\xaa\xc1$\x9e\x1e\x9c\tpk~\u058f\xabv\x1a\xc1\x90\x16\u0460(\xbb\xd0u\x88\x12\x11H\xea&e\x88\xf8\xf0\xe8T\x9d/m\xb0\xd5\\\xfb1)\x11e\x8f\x85\xe8\x06\x14\x9fg\xf9\xe4\xe4\xf8,\x8c\xc3\x1e\xc6}(\x14$.j\x1a\x14\x81\xcd0\xd3]\a\xe2r\xd6=\x88Z\x03\u008b\x0er\xf6\x05B\xe2hh\u0462\x16-@\xa7\xf6\xd4\xf0\u007fB\x13\xf4\xb6\xe9\xd0\x11FC\x9a?\x94\xa0\xb1\xfd\xae\x1e_\x8f\xeeoA\t\f\tU\u042e\u0397\u3998$\xc3\v\xda%\xed\xdc\f0\f['Xt\x9c\afG\xd6l\x1e\xb1\x99\t(Drtp\x01\rN\xcb\x01,\x8f\xdfE\xe6\xc0\xdc\xe0;\u0306U\x83%\xf0\xf3:\xb1p\x04*\x84\xe5ztG\xd0\x19v \x1b\xf6?\xe59q=\x12I\xd6\xd6\xd7\xd2v)\xa6M\aC\x94\x02p\x97K\xf4\x8e\u05a2\xf0\xb3\xe8S\xa7aV{\xce\xdbV\xb7\xeaE\x85\xccg\x85S\xc7q\x88M\xcb\xda\x18G\xba!\xce32\xa1\x01\xf9A'D@\xfb,\rE7v+\xb9`\xcc&\xa8L\xa9\xd5\xe0&\x98@\n2\xa5\xa05y\u06b5&p4\xa3\xb60\r\x98\x00T{\xd5\"\u011a\x17\xcenS\x1b\xd9\a\x0eI\x91+\xa3\x89\x97\xe9\x00\xdfr\xaby\xc8\xf62!`\x1eQ\xf5\x1c\x8b\u0549J\n \xec\x13\xb5\x12\x185`\xa0 ?\xe0\v\xbf\xecG7(\x82ZA\x9a[\x87tz|\x0e\x9e\x98\x0e\x9e\xa0\xa6\x85\nK\xcb\x1cY[\xc9\xc0\xa2\x1d\xd3\xffX#\x93\xb2sI\x96\xf5\xbfu4\xb5n\xa1\x14!\x16\xc2\x06p\xe9\u007f\x85\xa1E7eM\xc4\xf7{\xfd\x0f\xcb\xf2\x129wkQ\xba\xe5\x8eU\f\x99&\xa0\u07da\x14\xe5Dg\xe0\x99\xbc\xd6P\x88$\x13tx=f\xb8\xb4\x87\xc9\x04i'\xaa\xb0\xa0\x9dQw\xf1\u010b)B\x90L\x1c(N\x95\x89{\xa4\"\xa8\u0128u\x96i\x97\xc4=\x10Md\xcd\xe3\xd7\x10\x88EN\x01\x16\x04[\x1fb\xf5\x88m&\xa4A?$N-a\x97)\xfe\xaec##\v91\xe5\xa6C\xcf[\xba\xc0\xf5\xb4\xeas\x94{K\xbb\xe88Y\xd2:\u069c]5\x89\xf35KM\xfe\xb8\x05\t8-\x9d\a-\xba\x1d4\xfb\x06\x9eldlC5G\x15\xcfR\x88\xfd\x81\xff\xa8\xee\x1e&\xec7\xec\xd9\xf3\xd7\xf9\x06t\xe3\xa4\xc0\x82\u018f\x11Q\xbb#+\xb5\x00\x88'\xd7\x19!f9\xb4\v\xa1\xc3c:\f0\"\xe1L\xe2C\xccf\u0110\\L\xc1F\u07bf`\x1c,\xb0\xbf\xf1\x1eUq\xdd\xee\xa0\xdb\xff\x02c\ab\x92\xb8\xd1Y\x82\x00\x00\x01\x85iCCPICC profile\x00\x00x\x9c}\x91=H\xc3@\x1c\xc5_?\xb4\"\x15A;\x88\x88d\xa8N-\x88\x8a8j\x15\x8aP!\xd4\n\xad:\x98\\\xfa!4iHR\\\x1c\x05\u05c2\x83\x1f\x8bU\a\x17g]\x1d\\\x05A\xf0\x03\xc4\xc9\xd1I\xd1EJ\xfc_Rh\x11\xe3\xc1q?\xde\xdd{\u073d\x03\xfc\xf52S\xcd\xe0\x18\xa0j\x96\x91N&\x84lnE\b\xbd\xa2\x13\xc3\xe8C\x101\x89\x99\xfa\xac(\xa6\xe09\xbe\xee\xe1\xe3\xeb]\x9cgy\x9f\xfbs\xf4(y\x93\x01>\x81x\x86\xe9\x86E\xbcN<\xb5i\xe9\x9c\xf7\x89#\xac$)\xc4\xe7\xc41\x83.H\xfc\xc8u\xd9\xe57\xceE\x87\xfd<3bd\xd2s\xc4\x11b\xa1\xd8\xc6r\x1b\xb3\x92\xa1\x12O\x12G\x15U\xa3|\u007f\xd6e\x85\xf3\x16g\xb5\\e\xcd{\xf2\x17\x86\xf3\xda\xf2\x12\xd7i\x0e!\x89\x05,B\x84\x00\x19Ul\xa0\f\vqZ5RL\xa4i?\xe1\xe1\x1ft\xfc\"\xb9drm\x80\x91c\x1e\x15\xa8\x90\x1c?\xf8\x1f\xfc\xee\xd6,L\x8c\xbbI\xe1\x04\xd0\xf1b\xdb\x1f#@h\x17h\xd4l\xfb\xfb\u0636\x1b'@\xe0\x19\xb8\xd2Z\xfeJ\x1d\x98\xfe$\xbd\xd6\u04a2G@\xef6pq\xdd\xd2\xe4=\xe0r\a\x18x\xd2%Cr\xa4\x00M\u007f\xa1\x00\xbc\x9f\xd17\xe5\x80\xfe[\xa0{\xd5\xed\xad\xb9\x8f\xd3\a C]\xa5n\x80\x83C`\xb4H\xd9k\x1e\xef\xeej\xef\xed\xdf3\xcd\xfe~\x00i!r\xa3\xd0\xdb~d\x00\x00\x00\x06bKGD\x00#\x00#\x00#\xe7Ng`\x00\x00\x00\tpHYs\x00\x00\x0e\x81\x00\x00\x0e\x81\x01G\xb0.\x84\x00\x00\x00\atIME\a\xe4\x06\x1e\x13\x01.\xa5*\a\x9f\x00\x00\x1fUIDATx\xda\xed\x9dy|T\xf5\xf5\xf7\xdf\xf7f&\x1b\x83\x8ap\x91\x88\x82\x88\xa2\"U\x91\t.\x88\b\x89E\xad\xa2uP\xc6\xe5\xa7bE\xab\xe5\xe7\xf6\xf8h\xedX\xebF\xb5uy\xea\xda\u06aa\xd5\xd6\xea\xd02\xeek\u034c\x15\xab(3j]Z\x05\x15\xd9DB\u0092}\x99d\xee\xf3\u01fdX\x90,\x93d\x96\xbb\x9c\xf7\ub557H\xc8\xe4\x9e\xf3=\xe7s\xbf\xfbQ\x10\x1cAME\xb9\x17\x18\x03\f\a\xf6\x02\x86\x02#\x01\r\x18\f\f\x03\x86\x00\xa5\xe6W\xb1\xf9\xe5\x05T\xa0\x00H\x99_I\xa0\x1dh\x05\x9a\u036fF`#\xb0\x19\xa8\a\xd6\x02\x9b\x80o\xcc?\xaf\x01j\xb4h\\\x97\u05b0\x0f\x8a\xb8\xc0>\xd4V\x94\xab\xba\x91\xe4\x13\x80\xfd\x81}\u036f}\x80\x11f\"\xe7\x93V`\x05\xb0\nX\x06,\a>\x05\xfe\xadE\xe35\u0482\"\x00B\x9al\x98\xe1\xf7(\x8ar 0\x19\x98\x04\x1c\x02\x1c\x04\x94\xd8\u0524\xf5\xc0\xc7\xc0\a\xc0{\xc0R-\x1a_)--\x02 \x18]x\x1fp\x140\x158\xd2L\xfcR\x87\x9b]\r,\x01\xfe\t,F\xe1}\xad*\xde)\xd1 \x02\xe0\xfc\xee|e\xb9G\xd79\x1c8\x16\xa8\x00\x0e\x03<.wK\x1d\xb0\x18\xa8\x02^\u0562\xf1e\x12)\"\x00Nz\xcb\xef\n\x1c\a\x9cl&\xfe\x10\xf1J\x8f\xac\x00\x9e\a^P\x14\xfd\x1f\u00ea\x12\x1d\xe2\x12\x11\x00\xbb%\xbd\x06\x9c\n\x04\x80\xe9\xf2\x96\xef7\x9b\x81\xe7\x80E(\xbc\xaaU\u0153\xe2\x12\x11\x00+\x8f\xe7\x03\xc0\x99f\xf7\xbe@\xbc\x92Q6\x01\x8b\x80?\x93R\xde\xd2^_*K\x8f\"\x00\xf9\xa5z\xa6_Q;\x94c\x80\xf3\x80\xd98\u007f\x02\xcf*|\t<\n<\xa2E\xe3\xeb\xc4\x1d\"\x00\xb9~\xdb\x0f\x03\xe6\x02\x17b\xac\xc7\v\xf9\xa1\u04dc/\xf8-\x8a\xf2\x9aV%\xbd\x02\x11\x80\xec&\xfe\xa1\xc0\xa5\xc0\x1c\x8c\x1du\x82uX\x0e\xdc\v<\xaaE\xe3\x8d\xe2\x0e\x11\x80L%\xbd\x02\x9c\b\\\x05\x1c-\x1e\xb1<\x9b\x81\xdf\x01wk\xd1x\xb5\xb8C\x04\xa0_l\xa8(\xf7(p\x16p\rp\x80x\xc4v\xb4\x00\u007f\x04~\xa5E\xe3\xab\xc5\x1d\"\x00iQ;\xa3\xbcPW8\x17\xb8\x16c\x0f\xbe`o\xdaM!X\xa0E\xe3k\xc4\x1d\"\x00]w\xf5+\xfd\x05\xe8\u02b9\xc0\xcf1N\xd6\t\u03a2\r\xf8-\xf0K\xb7\x1fR\x12\x01\xd8q\x9c\u007f*p\x8bt\xf5]A=p;p\x97\x16\x8d7\x8b\x00\xb8;\xf1\x0f\x03\xee\x04\xa6\x887\\\xc7\x1a\xe0Z=\xc5\x13\xc3_w\xd7}\x06\x8a$~\xf9H\xe06\x8cI>\x11Dw\xf360_\x8b\xc6?\x10\x01p~\xe2\x17\x02\x97\x01\xd7\x03>\x89}\xc1\xa4\x13x\x00\xb8N\x8b\xc6\xebE\x00\x9c\x99\xfcS1\u0587\xc7K\xbc\v\u0770\xce\xec\r<-\x02\xe0\x9c\xc4\xdf\x05\xf8\x150O\xba\xfbB\x9aD\x80\x9f8u#\x91\xe2\xa2\xe4?\x11x\x10\xd8]bZ\xe8#\x1b\x81\x8b\xb5h\xfco\"\x00\xf6K\xfc\x9d\x81{\x80s$\x8e\x85\x01\xf2$p\x89\x16\x8do\x11\x01\xb0G\xf2\xcf\xc0\xd8\xf95JbW\xc8\x10\xab\x81\xff\u0462\xf1\xc5\"\x00\xd6M\xfcB\xe0&\xe0\xff\x92\xff\xab\xb2\x05\xe7\xd1\t\u072c\xe8\xfa\xcd\xc3b\x89\x94\b\x80\xb5\x92\u007f\f\x10\u01b8UW\x10\xb2I\f8\xd3\xce\x13\x84\xaa\u00d2\xff\x14\x8c{\xe7%\xf9\x85\\0\x03x\xaf\xa6\xa2\xfc(\xe9\x01\xe4\x91\u068a\xf2\x02\x1dn\xc58\xa7/\xcb{B\xaeI\x02\x97k\xd1\xf8\x03\"\x00\xb9\u007f\xeb\x0f3\xbb\xfc\x15\x12\x87B\x9ey\bc\x95 )\x02\x90\x9b\xe4?\bx\x169\xb2+X\x877\x80\xd9Z4^+s\x00\xd9M\xfeY\xc0[\x92\xfc\x82\u0158\x06,\xa9\xa9(\x1f'\x02\x90\xbd\xe4\xbf\x02x\x1a9\xc4#X\x93}\x80\xb7k*\xca-\u007f\xb4\xdcVC\x80\xea\x19\x93UU\xd1\u007f\x03\xfc\xaf\u0118`\x03Z\x813\xb4h\xfc\x19\x11\x80\x81\xbf\xf5\x8b\x81\xbf`\x94\xd9\x12\x04\xbb\xd0\t\\\xa4E\xe3\x0f[\xf1\xe1<6I\xfe\x9d0&\xfb\x8e\x91x\x12l(\x00\x1b\xa5\a\xd0\xff\xe4\x1f\n\xbc\nL\x92X\x12lF\x1bp\x9a\x16\x8d?/\x02\u043f\xe4\x1f\x81Q+\xfe@\x89%\xc1f\xb4\x00?\u0522\xf1W\xad\xfc\x90\x8a\x85\x93\u007f$\xc6^\xebq\x12K\x82\xcdh\x06fi\xd1x\xd4\xea\x0f\xaaX8\xf9_\a\xf6\x95X\x12lF#p\xa2\x16\x8d\xbfa\x87\x87U,\x98\xfc#0vS\u025b_\xb0\x1b\xf5\xc0\tZ4\xfe\x96]\x1e\xd8c\xb1\xe4\x1f\n\xbc&\xc9/\u0610-\xc0L-\x1a_j\xa7\x87\xf6X(\xf9\a\x03/\x03\x13$\x96\x04\x9b\xb1\t8V\x8b\xc6\u07f7\u06c3\xab\x16I\xfe\"\x8cu\xfer\x89%\xc1fl\x00\xa6\xdb1\xf9-!\x005\u01d6+\xc0c\xc0t\x89%\xc1f\xac\a*\xb4h\xfc#\xbb\x1a\x90\xff!@\x8a;\x819\x12K\x82\xcdX\a\xcc\u0422\xf1ev6\"\xaf\xab\x005\x15\xe5?\x01\xee\x93X\x12l\xc6\x1a\xf3\xcd\xff\xb9\xdd\rQ\xf2\x98\xfc\xc7\x03\xcf\x03\x05\x12O\x82\x8dXi\xbe\xf9\xbfr\x821J\x9e\x92\u007f<\xb0\x04\xd8I\xe2I\xb0\x11+\xcc\xe4_\xe5\x14\x83\x94<$\xff\x10`)\u01a5\t\x82`\x17>\u01d8\xed\xff\xdaIF\xe5t\x15\xa0v\xc6d\x15xB\x92_\xb0\x19\xff\x01\xa69-\xf9!\u01eb\x00\xba\xa2\xdf\x00\x1c'\xf1\x94sR\x18\xb3\u05b5\xe6\u007f7b\xdcV\u04c0\xb1w\xbd\r\xe3\xdcz\x01P\x88q\u055a\x0f(\x06v\x06\u0280a\xc0H\xf3\xef\xdc\xc4G\x18\x9b|68\u0478\x9c\r\x01\xccI\xbf\x17\x90R]\u0664\xc1\f\xd8\x0f\u0377\xd6\xe7\xc0\x17\xa0\xac\u0462K\x93\x19j\xc7\xe1\xc0\x18\x8c\x83Z\a`\x1c\xd5>\b\xe3rV\xa7\xd5dx\x1fc{o\xadS\x03F\xc9Q\xf2\xef\x81Q\xb1g\x98\xe4hF\xf9\x02X\f\xfc\x13x\aX\xa6E\xe3y\xa9Ug\xd6g\xf0\x03S\x80\xa9\xc0\x11fo\u00ae\xc4\xcd\xe4\xdf\xec\xe4\x00\u02ba\x00\xd4\u0318\\\x80\xa2\xbfn\x06\x8500\x9a0.Hy\x19x\xc5\u02b3\xd15\x15\xe5\xa5\xc0\xd1\xc0\xf1\xc0\xc9\xc0h\x1b\xf9y\tp\xbc\x16\x8d\xd79=\xa0\x94\x1c\x04\xc2/\x80\x1b$w\a\x94\xf4\xcf\x03\u007f5\x93\xbe\xc5n\x06\xd4TNV\xd0\xf5\x89\xc0\xe9\xc0\x19X\xbb\\\xfb\x9b\xc0\x0f\xb4h\xbc\xc1\r\xc1\xa5d9\xf9\x0f7\x1d\xea\x91<\xee\x13\xba\u9dc7\x81\xa7\xb4h\xbc\xd1)\x86\xd5T\xfa\x15te:0\x17\x98\x8d\xb5&\x15\xff\x81q\x99G\x93[\x02M\xc9b\xf2\xfb\xccq\xbf,\xf9\xa5O=\xf0(\xf0[-\x1a\xff\xcc\xe9\u019a\xf7?\\\x00\xcc\a\xf6\xc8\xf3\xe3Ta\\\xe3\xd5\u2980\u02e6\x00\xdc\x0f\\\"9\x9d\x16k\x81\xff\a<\xa4E\xe3\xf5n3\xbe\xa6\xb2\u0703\u0399\xc05\xc0\xf8<<\xc2K@@\x8b\xc6[\xdd\xe6{%K\xc9_\x81q\xb3\x8f\x94\xea\ue655\xc0/\x81\u01f4h\xbc\xdd\xed\u03a8\xfe\xbe_Q;\x95\xd31\xe6\x8c\xf6\xcf\u046f}NQ8}XU\xbc\u034d>W\xb2\x90\xfc\x830\u05a2\xf7\x96\xfc\xee>\u0581\x9b\xcc7~\xbb\xb8c\x87\x18*0\xe7\bn\x06Fd\xf1W-B\xe1L\xad\xca>\xe5\xbc\xed \x00w\x01WH\x18wI\vp\a\xf0k'M\xeceQ\b\x06\x03?\x03\xae$\xf3{\n\x9eD\xe1\x1c\xad*\xde\xe1f\x1f+\x19n\xb0C1\x0e\xfa\xc8\x11\xdf\x1dy\x1a\xb8R\x8b\xc6W\x8a+\xfa\x1cW\xfb\x03\xbf\xc3(\xbd\x9d\t\x1e\xd3\xe1G\u00e3\xf1N\xb7\xfb6c\x02\xb0\xa1r\x92\xaa\xe8\xea\xbb\x18\xbb\xc1\x84\xff\xb2\x06\xf8\x89\x95\xcbC\xd9B\x04\x8c\xbd\x04\x17\x02\xb7\x03\x83\a\xf0Q\x0f\xa3\xeb\x17j\xb1DJ\xbc\x9a\xc1}\xf9\x8a\xae^(\u027f\x1d:\xf0{\xe0@I\xfe\x81\xa3U-\u0575h\xfcA\xe0`\x8c=\x12\xfd\xe1w(\u0313\xe4\xcfp\x0f\xc0\\\xcf]\x0e\xec*.\x05\xe0\x1b\xe0|-\x1a\u007fE\\\x91yj+&\x17\xe8\xe8!\xe0\xfa>\f7\xef\x05\xfd2-\x9a\xd0\u0143\x99\x17\x00Y\xf3\xff//\x02s\xb5h\xbcF\\\x91\xf5\xb9\x81\xe9@\x18\x18\xde\xcb?\xbdS\x8b\u01af\x12\x8feA\x00j*\xca\x0f\xc48~\xea\xf6\x89\xbf\x0e \xa4\xe8\xa9\u06c7\xc5\u0793\xb7L\xeeD`\x0f\xe0)\xba\xaf)q\x9b\x16\x8d_+\x9e\xca\xde\x1c\xc0\xed\x92\xfc\xd4\x00\xdf\u05e2\xf1_K\xf2\xe7xn \x1a_\x8b\xb1:\xb0\xb0\x8bo\xdf(\u025fE\x01\xa8\xa9(\x9f\x81q\xdc\xd3\xcd|\x04L\u05a2\xf1\xd7%\x9c\xf2&\x02-\xaa\xae\x9c\x01\xfcj\x9b\xbf\xfe\x99\x16\x8d\xdf \xde\xc9\xd2\x10\xa0\xa6\u00af\x80\xf2.\xee.\xe7\xf520\xc7-GGm2$\xb8\x14\xf0h\xd1\xf8]\u236c\n@\xf9\x0f\u0371\x97[y\x04\xb8H\x8b\xba{'\x99\xe0\xc2!@\xe3\xadcT\x8c\xbd\xecn\xe5\xf6\x94\xae_ \xc9/\u061d~]\xd4QT\xbev\xb6245\xa1\xe5Y\r\xbd\xd5u\a\xfe\xae\u04e2\xf1\x05\x12:\x82\x13\xe8\xf3\xec}{\u032b(:O\x14\fi\xd8\xcd{@\x1b\x1d\xdf\xf8\xd0\x1b\\s\xd1\xef\xb5Z4~\xab\x84\x8d\xe0\xda!\x80\x92\xe2D\x8ck\xa0)\xd8e\v\x83\xcfX\x8ew\x92+.Q\xf9\xb9\x16\x8d\xdf&!#\xb8}\x0e\xe0\xa7\xdb\t\x82\xb7\x9dA\xc7.\xa3\xf8\xa4\xcdN\xbe\xf1\xff\x0e-\x1a\xbfE\xc2Ep\xb5\x00$\xab\xbcS\x80#w\xec\x16@\xf1\x84U\xf8\xce\xfb\x1ae\x88\xe3\xceY<\x86\xae_-\xa1\"H\x0f\xc0\xb8\x98\xa1[<\xbb\xd50\xf8\x9c/)\xd8\xcf1\x97\u073c\x06\xcc\xd3br\x80Dp&iO\xe1'\xab\xbc{c\x9c\xf8\xeb}\xe20\xa5\u04b2t\fm\xaf\x0f\xb6\xb3o\xfe\x03\x1c\xe1\xc6K:\x05\xe9\x01t\xc5%\xa4\xbbj\xa0\xa6(9\xfcKJ\xcf\u0600Rl\u02d7\xe7F\xe0$I~Az\x00\xc6\u06ff\x04\xf8\x1a\x18\xd2\xd7_\xd0Y\xb7\vM\xcf\xedAj\xadmj\x83tb\x94\x85zM\xc2C\x90\x1e\x80A\xb0?\xc9\x0fP\xb0\xf3\x16\x06\a\x97\xe3\xf5\xdbf\xa9\xf0\x06I~A\x04`{.\x1aP7c\xebR\xe1,\xcb/\x15\xfe=\xa5\"\xbb\xfc\x04\x19\x02l\xd3\xfd\x9f\x00|\x9c\xa9_\u0631A\xa3\xe9\xa92\xf4\u0356S\x82\r\xc0\xc1Z4\xbe^\xc2B\x90\x1e\xc0\u007f\x99\x9b\xc9_\xe8\x19^\xc3\xe0s\xbf\u0133\xbf\xe5\n\xb1\\ \xc9/H\x0f`\xbb\xb7\xbf\xc7\x03\xca\xd7\xf4~\xe7Z\xdfI\xa9\xb4\xc4\xc7\xd0\x16\xb3\xc4R\xe1\x1f\xb5h\xfc|\t\aAz\x00\xdb\xeb\xc3\xf7\xb3\x92\xfc`,\x15\x1ef.\x15\x96\xe4u\xa9p\x1d\xbdlp\x12\x04\xb7\x0e\x01\xce\xce\xf6\x03\x14\xee\xb5\x0e\xdf\u0715\xa8{\xe6\xedh\xfd\xffj\xd1\xf8\x16\t\x05A\x86\x00\xdbu\xff\xbd\xa5\x18E,}\xb9x\x10=YH\xf3\xe2\xbdH.-\u0365\xfd/h\xd1\xf8I\x12\x06\x82\xf4\x00v\xe4\a\xb9J~0\x97\n+\x96Sr\xf2\xa6\\-\x15\xb6\x02\x97I\b\b\"\x00]\x13\xc8\xc7\x03\x15\x8d_\x8do\xeeZ\x94]\xb3^\xb7\xf1.-\x1a_!! \xc8\x10`\xc7\xee\u007f\x11\xc6]\xf7y\x9b\xa2O\xb5\x94\xd2\xfc\xeah:>-\xca\xc6\xc7o\x00\xf6\x91\xdb|\x05\xe9\x01tME>\x93\x1f@-i\xc67k\x19E\x15Y9\x8fs\x93$\xbf t/\x00?\xb0\xc6\u04e5(\x99\xbc\x82AgVgr\xa9p%:\u007f\x90\xa6\x17\x84\xee\x05\xc0R3\xe3\xde\xd1\xdf\xe0;\u007f%\uaa0c,\x15.\xd0b\xf1vizA\xe8B\x00\x92U\xde\xf1\xc0\x9eV{\u0402\x9d\xea\x18\xac\xe2ig\u040c\u551c\xb2\xa9\xbf\xe5H\xef\u0422\xf1\xa44\xbb t/\x00\x15V\u007f\xe8\xa2\x03\u0325\u00a1}Z*\u0704Q\xceK\x10\x84\xae\x04\xc08\xfc\xc34;<\xb8G\xabe\xf0\xff|\x89g|\u06a7\n\xff\xa0E\xe3M\xd2\xe4\x82\xd0m\x0f@\xf1\x93\xc3\xdd\u007f\x03~\xf8\x92f|'-\xa3\xa8\xb2\u05e5\xc2N\xe0\x01inA\xe8y\bp\xb4\xfd,HQR\xbe\x82AgU\xa3\x94v\xbbT\xf8\xb2\x16\x8d\xaf\x96\xe6\x16\x84\x9e\x05`\x8a]\r\xf1\x8e\xfa\xc68U8\xaa\xcb9\xbe\x87\xa4\xa9\x05\xa1w\x018\xd2\xce\xc6|\xbbTx\xf8vK\x85\x1b\x80\x17\xa5\xa9\x05\xa1\a\x01HVy\xc7\x02\xc3\xecn\x90\xe2I2h\xfarJ~\xb8\x11<:@X\x8b\xc6;\xa4\xa9\x05\xa1\xe7\x1e\xc0d'\x19V\xb4\xff\x1a|s\xbf\xa6`\xef\xe4BifA\xe8]\x00\xfcN3\xce3\xacv\x9do\u03bf\x97H3\vB79\xb2\u035f\x0fq\xa0}\xcf\x15V&\xa5\xb0g\x1a\xccX\xd0X\x06\xec-\x9ep\x05\xef\xc7B\xbe\x96\xef\n\xc0\xc1\x0e4\xf4%i\ubd19\r\xdc#np\x053\x81\xbf\u007f;\x04HVy\u02c0\xa1\x0e3\xb2\x1dx]\xdaZ\x10v\xe0\x88\xef\xce\x01Lp\xa0\x91o{+\x93\x8d\xd2\u0582\xb0\x03\xe5\xdf\x15\x80\x03\x1ch\xa4\xbc\xfd\x05\xa1k\x0e\xfd\xae\x00\x8cs\xa0\x91\x8b\xa5\x9d\x05\xa1K\xcaf,h\xdcm[\x01\xd8\xcfa\x06&\x81w\xa4\x9d\x05\xa1[\x0e\xdaV\x00\xf6r\x98q\x1fz+\x93\xad\xd2\u0182\xd0-\a\x02\xa8\x1dU\x1e\x15\x18\xed0\xe3\x96J\xfb\nB\x8f\xec\a\xa0\xea({\x02^\x87\x19\xf7\xbe\xb4\xaf \xa4!\x00\xc0\b\a\x1a\xf7/i_A\u8471[\x05\xc0i\xdd\xff\x14:\x9fJ\xfb\nB\x8f\x8c\x9c\xbe\xa0\xa1@\x05\x86;\u0330\xd5\xdec\x93\xcd\u04be\x82\xd0#\x05\n\xcah'\n\xc0ri[AH\vM\x05\xca\x1cf\xd4W\u04ae\x82\x90\x16\xa3T\xf2\\\x044\vH\xc9oA\xe8C\x0f\xc0i\xa7\x00\xd7K\xbb\nBZ\xec\xa2\x02C\x1cf\xd4*iWAH\x8b2\x15\x1b\x15\x02I\x93\x06iWAH\x8b\xc1*P\xe20\xa3\xaa\xa5]\x05!-J\x9d(\x00\xb5\u04ae\x82\x90~\x0f\xa0\xc8A\x06u\xa2 5\x00\x04\xa1\x0f\x02P\xe8 \x83R\xe6\x97 \b\xbdS\xa2\xb2cy0;\xa3{+\xe4\x1apAH\x93B\xa7\t\x80\xbc\xfd\x05!}<*\xe0\xa47\xa6*m*\biS\xa0\x02\x9d\x0e2HIVy\x15iWAH/_T\x8c\v4\x9d\xd4\x03\x90^\x80 \xf4A\x00\u069c$\x00\n\x14H\xbb\nBZ\xe8*\xd0\xe2$E\xd3aWiWAH\x8b\x94\xd3\x04\x00\x9cw\xbf\x81 d\x8bN\x15hr\x98Q\x83\xa5]\x05!-\x92*\xb0\xd9aF\x8d\x92v\x15\x84\xb4hW\x81\x8d2\x04\x10\x04W\u04a4\xe2\xbc\xf3\xf3c\xa4]\x05!-\x1aT\x9cw\x85\xd6\xde\u04ae\x82\x90\x16\x8dN\x14\x80\xfd\xa4]\x05!-ZT\x9cw\x81\u01a8d\x95\xb7T\xdaV\x10z\xa5^\x05V:\xcc(\x15\xb3\xf4\xb1 \b=R\xad\xe2\xcc;\xf4\x0e\x91\xb6\x15\x84^\xa9Su\x85U\xe0\xb8k\xb4&I\xdb\nB\xaflQ\v+\x92\x9d\xc0Z\x87\x19v\x98\xb4\xad \xf4\xca\xea\xadGg\x9dVO\xef\xa0d\x95\xd7'\xed+\b\xbd\f\x01\xcc?,s\x98a*p\x94\xb4\xaf \xf4\x80\xce\u02ad\x02\xf0\xb9\x03\xcd;FZX\x10\xba\xa5\x05\xc5\xd8\b\x04\xf0\xa9\x03\r\xac\x946\x16\x84nY\x19\v\xf9\xf4\xad\x02\xf0\x89\x03\r\x9c\x98\xac\xf2\xee&\xed,\b]\xb2n\xebX\x19P\xd6\x02u\x0e\x9c\a\x98)\xed,\b]\xf2\xe5\xb7\x02\xe0\xadl\u05c1\x8f\x1ch\xe4)\xd2\u0382\xd0\xf5\x10\x00\xc0\xb3\xcd_|\x00Lu\x98\x913\x93U\xdeA\xde\xcad\x93\xb4w\xaf\x84\x81\xb7\xc4\r\xddr50\xc7A\xf6|\xf1]\x01H8\xad\xc5\x1aR\xc3J\x9fi:\xe0\x04\x88\xfdM\xe2\xb7gb!_\rP#\x9e\xe8\x9a\x19\v\x1a\x8b\x1cf\xd2\x0e\x02\x10w\x92u\xab\x92\xfbpc\xc38>My\x82\x80\b\x800P\x9ct\xc0L\xc7\xdc\xfb\xf3m\x15\x9d\x8eX\x81\xa2\xa7\xd4M\xc0.v\xb7\xec\u0356I\\\u07f4;m\x86y\xad@Y\"\x18\xd9\"1,\xf4\xf3\xed\xef\x03\xea\xb7\xcd\x17\x9b\xf3U,\xe4\xdb\x1b\xb6\xa9\xa2\xe3\x99\u0469\x03\xef\xd8\u066a6}0\x0f\xd5\x1f\xc35M#\xb7&?@1p\xba\x84\xb10\x00\xca\x1d\x94\xfc\x00\xff\xd9\xfa\x87\xef\x96\u0472\xed$\u0406\x8e=\xf9\u0656)<\u04beSW\xdf\xfe\x91\u01300\x00\xa68\u031en\x05\xe0M;Z\xf3a\xdb\xf78\xbf\xee`\xde\xee,\xec\xee\x9fL\xf6\x87\x03\x13%\x8e\x85~r\x8c\xc3\xec\xf9\xb0K\x01\xd0a\x899f\xb6\x05\x1dz!\x8b\x1a\xa7\xf0\xe3\x861l\xd4{\xad\tz\x89\u0131\u040f\xf1\xff \x9cw\xb0\xec\x83.\x05\xa0\xb02\xd9\x0e\xfc\xd3\x0e\x16\u0525v\u3dbai\xdc\xd9:4\xdd\x1f9\xcb\x1f\x0e\f\x93\x90\x16\xfaH\x05\xe0\xa4%\xc0&\x15\xf5\xb3\xee\x86\x00\x00Q\xab[\xf0er\x1c\x97l\xf1\xf3bGI_~\xacDz\x01B?p\xdan\xd2\xf7\xabB\xa5\xa9\x9e\x04\xe0U\xab>\xb9\x8eB\xb4\xb9\x9cs\xea\xf6gE\xaa_U\xc0\xe7\xfb\u00c1A\x12\xd3Bz\xdd\xff\xa6B\xe0d\x87\x99\xb5\xdd~\x9f\xae\x04\xe0_X\xf0\xa2\xd0\x16}g\x1e\xa8?\x86\xeb\x9a\xcbH\xf5\xffc4`\x9e\x84\xb6\x90\xe6+\xe78\x9cWn~I\x8f\x02\xe0\xadL\xea\xc0KVz\xe2o:Fs\xf5\x96#y\xbc=#\x85\u007f\xaf\xf1\x87\x03R7@H\x87\xf3\x1ch\u04d2\xdez\x00\x00\xcfY\xa6\xbf\xd2z0s\xeb\x0e\"\xd1\xe9\xcd\xd4G\x8e\x00\xe6Kl\v=w\xff\x1bG\x00':\u032c\xafb!\xdf\xd7\xe9\b\xc0k\xe4y90\xa9\x97\xf0d\xe3Q\\\xda8\x9a:=\u36f0~\xea\x0f\a\x86H\x98\v=0\x0f\xf0:\u0326\x1d\xf6\xf9t)\x00\xe6\xf1\u067cM\x06n\xee,\xe3\u6ea9\xdc\u04da\xb5\xe1\xd7\x10\xe0:\x89q\xa1+*\x164\x16\x02\x17;\u0434\xd7\xd3\x12\x00\x93E\xf9x\xc2e\xed\ap\u0456Cy\xad\xa38\u06ffj\xbe?\x1c\x90B\xa2\xc2\x0e\xe8p\x16P\xe6@\xd3b}\x11\x80\xe7r9\fHQ\xc0+\u03471\xb7~_\xd6\xe8\x05\xb9\xf8\x95\x85\xc0\xdd\x12\xee\u00b6L\xffec\x01\xc6\xe5\x1fN\xe3\xf3X\u0237:m\x01\xf0V&\ub057s\xf1dM\xa9]\xb9\xbbn\x1a76\uf19e[\xa7\xcc\xf4\x87\x03\xa7I\xd8\v[QtN\a\xf6w\xa0i\xaft\xf5\x97\xbdm\xa0\xffK\xb6\x9fjM\xc7\xde\\\xb9\xe50\xfe\x9a\xcc[!\x9f\xbbeBP\x00\x98qK\x83\a\xb8\xc1\xa1\xe6\xbd\xda\x1f\x01x\x11\xc8\xdaE\x1aKZ'r\u0796\x03\xf9(\x95\xd7\xc9\xd62\xe0.\t\u007f\x01E\x99\a\x8cs\xa0e\xadz\x17\x13\x80\xbd\n\x80\xb72\xd9\n<\x99\xe9\xa7i\xd7Ky\xb4\xe1h\xael\u0713fk\u0733p\x9e?\x1c8I2\xc0\xc5o\xff\x05\x8d;;\xf8\xed_\xf5z\xc8\xd7\u071f\x1e\x00\xc0\xa3\x99|\x92\xda\u0391\xfc|\xcbT\x1el\xb3\xdc\xcdc\u007f\xf0\x87\x03\xc3%\x15\\\xcb\r\x80S\xdb\xff\xd9\xee\xbe\u046b\x00x+\x93K\xc9P\u0340O\xda\x0ed\u0796\x89,\xee\xb4\xe4\xe9\xca\u0740G\xfdO\x04\x14\xc9\x05\u05fd\xfd\x0f\u01b9\xbbCS\xc0\xf3\xfd\x16\x00\x93\xdf\x0f\xe4\t:\xf1\xf0\\\xd3\x11\xcck\x18\xcbz]\xb5\xb2\xb3\x8eGu\xe4\x12\x90\xd0]\xf2\xdf\xd2P\x00\xfc\x81\xedo\xc8v\x12o\xc4B\xbe\xea\x81\n\xc0\x9f\x81\xc6\xfe\xfc\xf6\x86\xd40\xee\xa8;\x86[[4\xbb8\xec\x16\u007f8p\x8c\xa4\x86KP\x94\xab0.\xfdt*\x91\x9e\xbe\x99\x96\x00\x98{\x02\x1e\xef\xebo^\x99\u0707K\xb7L\u6664\xad\x0e\xdfy\x80\x85\xfep`\x94d\x87\xe3\xbb\xfe\a\x017:\xd8\xc4\x0e\xe0\xaf\x03\x16\x00\x93\xfb \xdd}:\n\x8b[&qn\xdd\x01|\x96\xb2e\xcfj8\xf0\xac\\\x1e\xe2\xe8\xe4/\x05\x9e\xc0Y\xd7}}\x97*\xb3\xe2\xd3\xc0\x05\xc0[\x99\xfc7\xc6)\xc1\x1ei\xd5\a\xf3\xfb\xfai\\\xd34\x92v{_\xa5~\b\xf0\xa4?<\xbb@\xd2\u0151\u070d\xb3\xaa\xfdt\u015fz\xfb\a}\x9d\x91\xbb\xb3\xa7oV\x9bw\xf3\xff\xb1\xeb\xbb\xf9\xed\xc8I\xa0\xdf#\xb9\u2e37\xff\xf9\xc0\x05\x0e7\xb3\x8e\x1e\x96\xff\xfa%\x00\x8a\xd1\x03\xf8\xb0\xab\xef\xfd\u02fc\x9b\u007fI\xf7w\xf3\u06d5K\xfc\xe1\xc0\xf5\x926\x8eI\xfe\u00c1\xfb]`\xea\xc2X7\x9b\u007f\xfa-\x00\x1e\u3eb0_m7\u02e0\x17\xb1\xa8q\n\x177\x8ca\x93\xb5\x97\xf8\x06\u008d\xfep\xe0RI\x1f\xdb'\xff(\xe0)\x8crqN\xe7\xa14_\xea}\xa3\xbd\xcaS\xa0\xa0|\x06\xecS\x97\x1a\xce=\xf5\a\xf3R\u07ee\xe7\xb6+:pI\"\x18\xf9\x9d\xa4\x92-\x93\u007f\b\xb0\x18\x98\xe0\x02s\u07cf\x85|\x93\xd2\xf9\x87}~e\x17Vvt\x02\xb7~\xd1>\x8e\x8b7\x97\xbb%\xf9\xb7\x8a\xe5\x03\xfep@\xee\x13\xb4_\xf2\x97b\xdco1\xc1%&\xa7\xfd\x92\xeaW\x9f\xbd\xbas\u061f\u03eb\xdf\u007f\xc5W\xba\xeb&\xc8\x15\xe0^\u007f8p\x8d\xa4\x95m\x92\xbf\bx\x1a\xe7\x95\xf7\xea\x8e\xcd\x18\u02db\xd9\x13\x80=f~\x93\uc11b\\\x1cW\xb7\xf9\u00c1;\x0e}b\x8e\x9c\x1b\xb0v\xf2\x17\x9bc\xfe\xef\xbb\xc8\xec\x87b!_SV\x05\xc0\x1c\x10?\xce6e\x86]\xc8\xffQ\u054e\xc7\xfd\xe1@\x91\xa4\x9a%\x93\u007f\x90\xd9\xed?\xc1Efw\x00\xf7\xf6\xe5\a\xfa-\x00\xef\x05#\x9d@\xc8\xe5qv&P%\u01c8-\x97\xfcC\x81*\xe0X\x97\x99\xbe0\x16\xf2\xad\u0249\x00\x00$\x82\x91g\x80\xb7\\\x1eoG\x01K\xfd\xe1\xc0DI=K$\xffX\xe0m\xe0p\x17\x9a\u007f{_\u007f \x13\v\xf7WB\xae\xef\xf2\xb4\x1c\xa3\x81\xb7\xfc\xe1\xc0\\I\xc1\xbc&\xff1\xc0\xbb8\xf3Z\xaf\xdex1\x16\xf2}\x98s\x01H\x04#K\xe9\u00ec\xa3\x83)\x01\x1e\xf1\x87\x03\x8f\xca!\xa2\xbc$\xffe\xc0\u07c1\xa1.u\xc1-\xfd\xf9\xa1\x8c\xccb\xfb\u00c1\x91\xc0g\x80OB\x11\x80\xe5\xc0\u0649`$.\xae\xc8z\xe2\xef\x82q\xa1\xc7l\x17\xbb\xe1\ufc50of\u007f~0#{w\x13\xc1\xc8\xd7\xfdU \x872\xce\x1c\x12\xdc\xe4\x0f\xcf.\x14wd\x87\u024f_=UW\xdb?py\xf2\x03\xf4\xfb\xacJ\xc6\u05b1\xfd\v\x03\x85\xe8|\x883\x8b*\f\x84O\x80\v\x12\xc1\u023b\xe2\x8a\f\u015aQ\xde\xfd&\xe0\n59^-]?\x0f\xb5}W\xb7\xba\xe3\xb9X\xc8wr\xde\x05\xc0l\x98i\x18\xf7\x8f\xcb\x06\x99\xedIa\u072bx]\"\x18\xd9(\xee\x18P\x8c\xcd\xc48\xcd7\xf6\xdb \xd6w\xa2\xb8f>\xde\x06\u05d5z\xec\x04\x0e\x89\x85|\x9fXB\x00\xcc\x06z\x04\x90\xd9\xf0\xae\xd9\b\xfcBQ\x94\a\xe3s\x16u\x88;\xfa\x14Wc\x80_\xf7\xd4\xdd/\xac?\x9b\xe2\xda\x19\xe0\x9e-\xea\x8f\xc4B\xbe\x1f\r\xe4\x03\xb2!\x00C0v\b\x8e\x90\xb0\xed\x96e\x18\x9b\xa8\x9eJ\x04#\xba\xb8\xa3\xc7x\xda\x15\xb8\x06\xb8\x944\x8e\xf1\x16\xb4\x1dA\xe9\xfa\xb3Q:\x1c?\x1f\xdd\b\xec\x17\v\xf9\xd6YJ\x00\xccF;\x95^n#\x15\x00x\x0f\xb8\x91\x94\xfeB\xe2\u0327D\b\xb6\x8f\xa1\x9d\x81\xcb\u036f>U\x91Q:\xcb(\xdd0\x9f\x82\xe6=\x9c\xec\xa2\xebc!\xdf\xcd\x03\xfd\x10%\x8b\r\xf8\x04p\x86\x84rZ|hvo\xff\x9a\bF:\\\x9e\xf8e\xe6\xdb\xfeb`\xe7\xfe\u007fR\x01\u015b/\xa4p\x93#7\x04~\x05\xca\xf8XhP\xab\x95\x05`W\xe0c`w\xc9\xef\xb4Y\x03<\x00<\x9c\bFj\\\x96\xf8G\x00\x97\x00\xa7\x03\x19[:\xf56\x1fGq\xf5\xa9()G\x9d\xd9:%\x16\xf2=\x9b\x89\x0fR\xb2\u07283\x81\x97\x91U\x81\xbe\xd2\x06<\x03<\x82\xa2T%\xe6,J94\xe9w\x03\xce\x02\xce\x03\xbe\x97\xad\u07e3v\xecG\xe9\xfa\vQ\u06c69\xc1m\xcf\xc7B\xbeY\x99\xfa0%\a\x8d\xfc\x1b\xe02\xc9\xe9~\xb3\x0e\b\x03\x8bR\xaa\xfe\xce\xfb\xa7\xdb{\xae\xc0<99\v\x98\x03L\ar2e\xaf\xe8\x83(\xae\x9d\x8f\xb7~\xbc\x9d\xdd\xd7\bL\x88\x85|\xab\xec$\x00\xc5\xc0;\xc0\xc1\x92\xcb\x03\xe6k\x8cB\x8f/\x01\xb1D0\xd2d\xf5\a\x9e\x14\x0ex\x14\x98\x84q)\xc7\xf1\xc0adh\aj\u007f(l\x98Cq\xcdL\xbb.\x15^\x11\v\xf9~\x93Qa\u0311\xea\xef\a$\x90\xb3\x02\x99$\t,\xc1\xb8\xe8\xf2-`i\"\x18\xd9d\x817\xfc\x10\xc0o&\xfa\x14\xe0H\xc0R\x85\"\n\xda\xca)\xa9>\x1759\xd8N\xed\xfd6*Sc\xd7\xfaR\xb6\x13\x0030\xce\x04\xfe\"y\x9b5t\xe0K\xe0\x03\x8cU\x85O\x81/\x14X\x1e\x0fFZ3\u0716*F9\xf5\xbd1\xce=\xec\x8bQe\xe7 \x8c\xa3\u0456\x9f\xf3QR\xc3)\xd90\x1fO\xd3h;\xb4m\v01\x16\xf2-\u02f8\x1fr\xfcv\xb8\x1fc\xa6W\u022d0\xd4\x02\xab\x80\x1a`\xad9\x96\xac\x03\xb6\x00\xcd\x18[JSf<\xa8\x18\xb3\xf0\x830\x96\xe1\x06\x03\xa5@\x19\xa0\x99_\xa3pD9m\x95\xa2-\x17P\xb4\xf1H\xabk\xd6\u5c50\xef\xeel|p\xae\x1b\xf1\n`\"p\x84\xe4e\xee^v\xdb$\xae\xb0\x1d)\xdav\xf9=\x9dE+(\xa9>\r\xa5\u04d2\xf5B\xaatU\xb9'\x9b\xc1\x91\xeb1\xe2\xee@\x1c\xd9\x1f X\xa9/\xd01\x96\x92\xea\x1fS\xd0j\xa9\xeb\x1dk\x81\x83\a\xba\xdd\xd7R\x02`\x8a\xc0\x11@\fw\x94h\x12l3X*\xa5d\xe3\xc5x\xeb\x0e\xb2\xca\xd0mV,\xe4{!\x9b\xbf$/k!\xeb\x16}\xbav\xf7\xd9\xe3W\x02\xa7J\xd4\t\xd6\x19,%\xe9(]\x82^X\x80\xa7y,y\\\xad\x04\xb8+\x16\xf2\u0757\xf5\x9eO\xbe\xacK\x04#\u007f\xc1\xdd\xc5E\x04\x8b\xd2\xee\x8b\u0434\u01fd\xa4\xbcu\xf9z\x84\u007f\x02?\xcd\xc5/\xca\xebn\x88Q\xa7Ox#\xa5\xebc1\x96\x8f\x04\xc1:\xa3\x81\x82\xf5$\a\xbfKA\xc78\xd4\xf6!\xb9\xfc\xd5\xeb\x81\xcaX\xc8W\x9f\x93NO\xbe\x1d\xed\x0f\a\n\x81\x17\x81J\t;\xc1\x82\xe3\x02\x8a\xea\xe6RT{t.\u04a5\r\x98\x11\v\xf9\xde\u039du\x16\xc0\x1f\x0e\xec\x84q\x95\u0621\x12p\x82\x15\xf1\xb4N\xa3d\xfd\x19(\x9dY\xad\x86}A,\xe4{8\xb7\xf2f\x11\xccC\"o\xe2\u03a2\x0e\x82\rP;\xf72\x96\n[\u02b2\xf1\xf1w\xc6B\xbe\xabrn\x93U\x9c\x9b\bF6`\xd4r[)\xa1&X\x91T\xc1J\x9a\xca~A\xfb.\x1fd\xfa\xa3\x9fF\u05ef\xce\xcf\x00\xc7b\xf8\u00c1\xb1\x18\a\\d\xa3\x90`Y\xbcM\xb3(\xde0\v%\xe5\x1d\xe8G-\xc1\x98\xf4k\x16\x01\xf8\xaf\b\x8c\x03\xfe\x81\xb1\xff\\\x10,IA\xf2{\x94\xac\xff\xd1@V\t>\x05\xa6\xc6B\xbe\xbc]\x15o\xd9\x13\x10\xe6\x11\xe2\x98\xf4\x04\x04+\xa3\xa4v\xa1\xa4v>\x9e\x86}\xfb\xfa\xa3\xab\x81\xa3\xfaZ\xce\xdb5\x02`\x8a\xc0>\xa6\b\xec)\xa1&X\x99\xa2\xfas(\xaa\x9d\x0ezZ\xd3j\xdf\x00\xd3b!\xdf\xe7y\x170\xab;\xd6\x1f\x0e\x8c\x06^\xc38s.\b\x96\xc5\xd3:\x85\x92\xea\xb3P:z,\x0e\xbd\x01c\xad\xff\u07d6\xe8\xc1\xd8\xc1\xb1\xfep`\x04\xc6\u5887H\x98\t\x96\x1e\x12t\x8e\xa4t\xc3O(h\x1e\xd9\u0577k\xcc\xe4\xff\xc42\xcfk\x17\u01da\x85\"\x9e\x03\x8e\x960\x13,\x8d\xee\xa5x\xf3E\x14n.\xdf\xf6o\xd7\x03\xc7Z)\xf9m%\x00\xa6\b\x14\x01\u007f\u00b8;^\x10,\x8d\xb7\xf9\x04\x8a\xab\u007f\x88\x92*\\\x83\xb1\u0537\xdcj\xcfh\xab\xabQ\xd7-\xfa\xb4sd\xe0\x80\xa7P\x94A\x18\x97M\n\x82eIy?\xa7\u04f7jya\u07513b!\xdf\nK\x0eY\xec\xea\\\u007f80\x0f\xb8\x8f\fV\x91\x11\x84\f\xb3\x148\xd1\xcaU\x9el]\xb1\xc7\x1f\x0eL\x03\x16\x01\xc3$\xd6\x04\x8b\xf1\x02\x10\xb4z\xed\x06\xd5\xce\x1eN\x04#o\x00\xe5\xc0\xbf$\xde\x04\vq?\x8ar\x8a\x1d\n\xb78\xa2f\x9f\u007fa\xa0\x14\x9d\a\x81\xb3%\xf6\x84<\xd2\x01\\\x9e\bF\xee\xb7\xcb\x03;\xaah\xa7?\x1c\xf81\xf0\x1b\xa0HbQ\xc81\xb5\xc0\x9cD0\x12\xb3\xd3C;\xaej\xaf?\x1c\x98\b,Dv\x0e\n\xb9\xe3= \x90\bFV\xd9\xed\xc1U\xa7\xb5D\"\x18\xf9\x00\xa3\x18\xe5\x9f$.\x85\x1c\xf0 0\u014e\xc9\xef\xc8\x1e\xc0wz\x03A\xe0\x01`\x88\u0129\x90a\ua04b\x12\xc1H\xd8\xceF(No%\u007f8\xb0\a\xf00FyjA\xc8\x04K\x80\xb3\x13\xc1\xc8\n\xbb\x1b\xa2:\xbd\xa5\x12\xc1\xc8Z\x1d\xf58\xe0BS\xb5\x05\xa1\xbf$\x81\xebu\x98\xea\x84\xe4wE\x0f\xe0;\xbd\x81=\x81\xfb\x81\x93$\x96\x85>\xf21p\xae9\xc7\xe4\x18\x147\xb6\xa4?\x1c8\rc\xb9Pn\x1b\x12z\xa3\r\xf8%pk\"\x18I:\xcd8\u016d\xadj\xd6\"\xb8\x11\x98\x8f#j\xdd\vY\xe0\r\xe0\u01c9`\xe43\xa7\x1a\xa8\xb8\xbd\x85\xfd\xe1\xc0\x81\xc0\xdd@\x85\u013b`\xb2\x1e\xb8:\x11\x8c\xfc\xd9\xe9\x86*\xd2\xd6\xdf\n\xc1,\xe0\xd7\xc0~\xe2\rWw\xf7\xef\x06\x16$\x82\x11WL\x18\x8b\x00lCy8\xe0\u044d\u0542\x9f\x03#\xc4#\xaeA\a\xfe\x06\\\xeb\x94\xd9}\x11\x80\x81\xf5\x06\x06\x01\x97\x01W!\x9b\x88\x9cN\x95\x99\xf8\t7\x1a/\x02\u0433\x10\xecl\n\xc1\xe5\"\x04\x8ec1\xf0\x8bD0\xf2\x0f7;A\x04 =!\xd8\t\xb8\xc4\x14\x82\xdd\xc4#\xb6\xe65s\x8c\xff\x86\xb8B\x04\xa0\xafBP\f\x9c\v\\\x81L\x16\u0689Ns\x8c\u007fG\"\x18yO\xdc!\x020 \x0e[\x18P:uN\x00.\u0168h,~\xb4&\x9b0\u0381\u0717\bFV\x8b;D\x00\xb2\xd1+\xd8\x0f\xb8\b8\a\x18*\x1e\xb1\x04\xefb\x1c\xd3]\x98\bF\x9a\xc5\x1d\"\x00\xb9\x10\x82\" \x00\x9c\x87\xb1\xa9H\x15\xaf\xe4\x94\x1a\xe0\t\xe0\x91D0\xf2\x91\xb8C\x04 \x9fb\xb0\a\xc6\xfd\x84g\x02\xdf\x13\x8fd\x8d&\xe0y\xe0\tt\xe5\x95\xc4\x19\x8b\x92\xe2\x12\x11\x00\xab\x89\xc1\x81\xc0i\xe6\xd7x\xf1\u0200\xa9\a^\xc1\x98\xd4{\xd9\x0e7\xef\x8a\x00\b[\xc5`\x1c0\xcb\xfc:\x12\x9bUf\xca#+0\x8a\u00fe\x88\xaeG\x13g<\xd5..\x11\x01\xb0\xbb\x18\f1\xe7\nf\x9a\xff\x1d#^\xf9\x96\xcd\x18'\xf1\xa2\xc0\xab\x89`\xe4sq\x89\b\x80\xd3\x05a\f0\r\xa3\xfa\xf1\x91\xc08\x97\xb4\x8f\x0e\xac\u01b8f\xeb-\xe0MP>N\x04\x17\xa5$*D\x00\xdc,\b\xc30\xaa\x1e\x95\x03\x87\x02\x13\x81Q67+\x05\xac\x04>\x04>\x00\xde\a\x12\x89`\xa4ZZ\\\x04@Ho\xd80\x1e\x98`\xfew\x9c9t\x18\x8b\xb5.5\xd9d&\xfa2\xe0+\xf3\xbf\xff\x01\xe5\xb3DpQ\xa3\xb4\xa4\b\x80\x90Iaxr\xb6\aE/\x03\xf6\x02F\x9a_#\x00\r(\x03v\x02v\x06\x86\x03\x83L\xb1HG0R\x18\xdbh\x93\x18\xb3\xef\xb5@\x83\xf9Um\xfe\u007f\r\xf0\r\xb0\x0e\xf8\x1aX\x95\bF$\xc9E\x00\x04\x8b\xf6\"\x14\xa3\xdd\x15\x05\xf4b\xa0X\xd7\x15\x8f\xaa\xea*\x80\xae\xd3\x01\xb4\xeb\xba\u04a2(z\a\xa0'\x82\x11]<\xe7L\xfe?\xbb\x1b\xa3\xf3\x9b\u044a\x18\x00\x00\x00\x00IEND\xaeB`\x82") + +func googlePngBytes() ([]byte, error) { + return _googlePng, nil +} + +func googlePng() (*asset, error) { + bytes, err := googlePngBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "google.png", size: 15501, mode: os.FileMode(436), modTime: time.Unix(1593543714, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -123,6 +141,7 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "tailscale.png": tailscalePng, + "google.png": googlePng, } // AssetDir returns the file names below a certain @@ -166,6 +185,7 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ + "google.png": &bintree{googlePng, map[string]*bintree{}}, "tailscale.png": &bintree{tailscalePng, map[string]*bintree{}}, }} diff --git a/cmd/tailscale/main.go b/cmd/tailscale/main.go index 5b8f47f..5a8e44d 100644 --- a/cmd/tailscale/main.go +++ b/cmd/tailscale/main.go @@ -23,7 +23,7 @@ import ( "tailscale.com/wgengine/router" ) -//go:generate go run github.com/go-bindata/go-bindata/go-bindata -nocompress -o logo.go tailscale.png +//go:generate go run github.com/go-bindata/go-bindata/go-bindata -nocompress -o logo.go tailscale.png google.png type App struct { jvm jni.JVM @@ -84,8 +84,14 @@ type SearchEvent struct { type ReauthEvent struct{} +type GoogleAuthEvent struct{} + type LogoutEvent struct{} +// serverOAuthID is the OAuth ID of the tailscale-android server, used +// by GoogleSignInOptions.Builder.requestIdToken. +const serverOAuthID = "744055068597-hv4opg0h7vskq1hv37nq3u26t8c15qk0.apps.googleusercontent.com" + const enabledKey = "ipn_enabled" func main() { @@ -218,6 +224,9 @@ func (a *App) runBackend() error { alarm(a.notifyExpiry(service, m.Expiry)) } } + case tok := <-onGoogleToken: + // TODO(elias.naur) + _ = tok case <-alarmChan: if m := state.NetworkMap; m != nil && service != 0 { alarm(a.notifyExpiry(service, m.Expiry)) @@ -569,6 +578,14 @@ func (a *App) processUIEvents(w *app.Window, events []UIEvent, peer jni.Object, a.request(e) case CopyEvent: w.WriteClipboard(e.Text) + case GoogleAuthEvent: + err := jni.Do(a.jvm, func(env jni.Env) error { + sid := jni.JavaString(env, serverOAuthID) + return a.callVoidMethod(peer, "googleSignIn", "(Ljava/lang/String;)V", jni.Value(sid)) + }) + if err != nil { + fatalErr(err) + } case SearchEvent: state.query = strings.ToLower(e.Query) a.updateState(peer, state) diff --git a/cmd/tailscale/ui.go b/cmd/tailscale/ui.go index 649943a..0feb4bd 100644 --- a/cmd/tailscale/ui.go +++ b/cmd/tailscale/ui.go @@ -33,6 +33,8 @@ import ( _ "image/png" ) +const enableGoogleSignin = false + type UI struct { theme *material.Theme store *stateStore @@ -43,7 +45,11 @@ type UI struct { enabled widget.Bool search widget.Editor - signin widget.Clickable + // webSigin is the button for the web-based sign-in flow. + webSignin widget.Clickable + + // googleSignin is the button for native Google Sign-in + googleSignin widget.Clickable self widget.Clickable peers []widget.Clickable @@ -74,6 +80,7 @@ type UI struct { search *widget.Icon more *widget.Icon logo paint.ImageOp + google paint.ImageOp } events []UIEvent @@ -94,6 +101,7 @@ const ( headerColor = 0x496495 infoColor = 0x3a517b white = 0xffffff + signinColor = 0xe1e3e4 ) const ( @@ -122,6 +130,14 @@ func newUI(store *stateStore) (*UI, error) { if err != nil { return nil, err } + googleData, err := googlePngBytes() + if err != nil { + return nil, err + } + google, _, err := image.Decode(bytes.NewReader(googleData)) + if err != nil { + return nil, err + } face, err := opentype.Parse(robotoregular.TTF) if err != nil { panic(fmt.Sprintf("failed to parse font: %v", err)) @@ -135,6 +151,7 @@ func newUI(store *stateStore) (*UI, error) { ui.icons.search = searchIcon ui.icons.more = moreIcon ui.icons.logo = paint.NewImageOp(logo) + ui.icons.google = paint.NewImageOp(google) ui.icons.more.Color = rgb(white) ui.icons.search.Color = ui.theme.Color.Hint ui.root.Axis = layout.Vertical @@ -169,7 +186,11 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat } } - if ui.signin.Clicked() { + if ui.googleSignin.Clicked() { + ui.events = append(ui.events, GoogleAuthEvent{}) + } + + if ui.webSignin.Clicked() { ui.events = append(ui.events, ReauthEvent{}) } @@ -289,12 +310,47 @@ func (d *Dismiss) Dismissed(gtx layout.Context) bool { // layoutSignIn lays out the sign in button(s). func (ui *UI) layoutSignIn(gtx layout.Context) layout.Dimensions { - return layout.Inset{Top: unit.Dp(48)}.Layout(gtx, func(gtx C) D { - return layout.Center.Layout(gtx, func(gtx C) D { - signin := material.Button(ui.theme, &ui.signin, "Sign In") - signin.Background = rgb(headerColor) - return signin.Layout(gtx) - }) + return layout.Inset{Top: unit.Dp(48), Left: unit.Dp(48), Right: unit.Dp(48)}.Layout(gtx, func(gtx C) D { + return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func(gtx C) D { + if !enableGoogleSignin { + return D{} + } + signin := material.ButtonLayout(ui.theme, &ui.googleSignin) + //signin.Background = rgb(headerColor) + signin.Background = rgb(signinColor) + + return layout.Inset{Bottom: unit.Dp(16)}.Layout(gtx, func(gtx C) D { + return signin.Layout(gtx, func(gtx C) D { + gtx.Constraints.Max.Y = gtx.Px(unit.Dp(48)) + return layout.Flex{Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func(gtx C) D { + return layout.Inset{Right: unit.Dp(4)}.Layout(gtx, func(gtx C) D { + return drawImage(gtx, ui.icons.google, unit.Dp(16)) + }) + }), + layout.Rigid(func(gtx C) D { + return layout.Inset{Top: unit.Dp(10), Bottom: unit.Dp(10)}.Layout(gtx, func(gtx C) D { + l := material.Body2(ui.theme, "Sign in with Google") + l.Color = ui.theme.Color.Text + return l.Layout(gtx) + }) + }), + ) + }) + }) + }), + layout.Rigid(func(gtx C) D { + label := "Sign in with other" + if !enableGoogleSignin { + label = "Sign in" + } + signin := material.Button(ui.theme, &ui.webSignin, label) + signin.Background = rgb(signinColor) + signin.Color = ui.theme.Color.Text + return signin.Layout(gtx) + }), + ) }) } @@ -338,14 +394,7 @@ func (ui *UI) layoutIntro(gtx layout.Context) { // "tailscale". layout.Rigid(func(gtx C) D { return layout.N.Layout(gtx, func(gtx C) D { - img := ui.icons.logo - img.Add(gtx.Ops) - sz := img.Size() - aspect := float32(sz.Y) / float32(sz.X) - w := gtx.Px(unit.Dp(200)) - h := int(float32(w)*aspect + .5) - paint.PaintOp{Rect: f32.Rectangle{Max: f32.Pt(float32(w), float32(h))}}.Add(gtx.Ops) - return layout.Dimensions{Size: image.Pt(w, h)} + return drawImage(gtx, ui.icons.logo, unit.Dp(200)) }) }), // Terms. @@ -730,6 +779,16 @@ func drawLogo(ops *op.Ops, size int) { row.Pop() } +func drawImage(gtx layout.Context, img paint.ImageOp, size unit.Value) layout.Dimensions { + img.Add(gtx.Ops) + sz := img.Size() + aspect := float32(sz.Y) / float32(sz.X) + w := gtx.Px(size) + h := int(float32(w)*aspect + .5) + paint.PaintOp{Rect: f32.Rectangle{Max: f32.Pt(float32(w), float32(h))}}.Add(gtx.Ops) + return layout.Dimensions{Size: image.Pt(w, h)} +} + func drawDisc(ops *op.Ops, radius float32, col color.RGBA) { defer op.Push(ops).Pop() r2 := radius * .5 diff --git a/go.mod b/go.mod index 970015b..a7fcd31 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b - gioui.org v0.0.0-20200622162632-3a542cc80cee + gioui.org v0.0.0-20200630184435-6ef1ff7cfbfb gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43 github.com/go-bindata/go-bindata v3.1.2+incompatible github.com/tailscale/wireguard-go v0.0.0-20200615180905-687c10194779 diff --git a/go.sum b/go.sum index 49c37fc..30b327f 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b h1:J9r7EuPdhvBTafg34EqrObAm/bDEaDh7LvhKJPGficE= eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b/go.mod h1:CYwJpIhpzVfoHpFXGlXjSx9mXMWtHt4XXmZb6RjumRc= gioui.org v0.0.0-20200622101735-5368743478e0/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= -gioui.org v0.0.0-20200622162632-3a542cc80cee h1:qJXyQltqYTE3EURAc5ud8alfncvhIHKhsq/ooxdsqMo= -gioui.org v0.0.0-20200622162632-3a542cc80cee/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= +gioui.org v0.0.0-20200630184435-6ef1ff7cfbfb h1:+jJBzbEtW03w0+fAfhPCwmN0/8xN1CJ94lbfV2eSKhs= +gioui.org v0.0.0-20200630184435-6ef1ff7cfbfb/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43 h1:Wj8OoCIw06dNSSSPAAipnmcG7dbFn+7Et9IY37e1HBU= gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43/go.mod h1:KrsGUGWoPetiRyuDmOd/GTNCBFi2u4UbESTFXZ5YqXY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=