Update maps, add login basics

This commit is contained in:
mar-v-in 2015-02-01 23:27:46 +01:00
parent 2539e4b3be
commit ff1d6e928d
78 changed files with 1059 additions and 114 deletions

9
.gitignore vendored
View File

@ -1,5 +1,8 @@
*.iml
gen
bin
build
gen/
bin/
build/
user.gradle
.gradle/
gradlew
gradle/

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2014 μg Project Team
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@ -21,28 +20,32 @@
android:versionName="1.0"
android:versionCode="6599436">
<uses-sdk android:minSdkVersion="16" />
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<uses-permission android:name="android.permission.ACCESS_COARSE_UPDATES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<application
android:theme="@style/AppTheme"
android:label="@string/gms_app_name"
tools:replace="android:label">
<uses-library
android:name="com.google.android.maps"
android:required="false" />
<meta-data android:name="fake-signature"
android:icon="@drawable/ic_microg_app"
tools:replace="android:label,android:icon">
<meta-data
android:name="fake-signature"
android:value="@string/fake_signature" />
<!-- Location -->
<service
android:name="com.google.android.location.internal.GoogleLocationManagerService"
android:permission="android.permission.ACCESS_COARSE_LOCATION"
@ -53,17 +56,43 @@
<intent-filter>
<action android:name="com.google.android.location.internal.GMS_NLP" />
</intent-filter>
<meta-data android:name="minProtocolVersion"
<meta-data
android:name="minProtocolVersion"
android:value="1" />
<meta-data android:name="maxProtocolVersion"
<meta-data
android:name="maxProtocolVersion"
android:value="1" />
<meta-data android:name="releaseVersion"
<meta-data
android:name="releaseVersion"
android:value="2007" />
<meta-data android:name="nlpServiceIntent"
<meta-data
android:name="nlpServiceIntent"
android:value="com.google.android.location.internal.GMS_NLP" />
</service>
<activity android:name="org.microg.tools.AccountPickerActivity"
<!-- Auth -->
<service
android:name="org.microg.gms.auth.loginservice.GoogleLoginService"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gsf.action.GET_GLS" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
<meta-data
android:name="android.accounts.AccountAuthenticator.customTokens"
android:value="1" />
</service>
<activity
android:name="org.microg.tools.AccountPickerActivity"
android:exported="true"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.Holo.Dialog">
@ -73,9 +102,22 @@
</intent-filter>
</activity>
<service android:name=".auth.GetToken"
<activity
android:name="org.microg.gms.auth.login.LoginActivity"
android:theme="@style/LoginBlueTheme"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.login.LOGIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service
android:name=".auth.GetToken"
android:exported="true" />
<activity android:name=".auth.TokenActivity"
<activity
android:name=".auth.TokenActivity"
android:exported="true" />
</application>
</manifest>

View File

@ -1,3 +1,19 @@
/*
Copyright 2013-2015 µg Project Team
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
buildscript {
repositories {
mavenCentral()

11
local.properties Normal file
View File

@ -0,0 +1,11 @@
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/home/marvin/Applications/Android SDK
ndk.dir=/home/marvin/Applications/AndroidNDK

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2014 μg Project Team
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@ -15,5 +14,4 @@
~ limitations under the License.
-->
<selector>
</selector>
<selector />

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2014 μg Project Team
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@ -15,5 +14,4 @@
~ limitations under the License.
-->
<selector>
</selector>
<selector />

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2014 μg Project Team
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@ -15,5 +14,4 @@
~ limitations under the License.
-->
<selector>
</selector>
<selector />

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2014 μg Project Team
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@ -15,5 +14,4 @@
~ limitations under the License.
-->
<selector>
</selector>
<selector />

62
res/layout/login_base.xml Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/auth_root"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/title_container"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="64dp">
<TextView
android:layout_alignParentBottom="true"
android:textColor="@color/primary_text_default_material_dark"
android:textSize="24sp"
android:padding="18dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:id="@+id/title"
android:text="@string/just_a_sec"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
<FrameLayout
android:id="@+id/auth_content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<include layout="@layout/login_loading" />
</FrameLayout>
<View
android:background="#e4e7e9"
android:layout_width="match_parent"
android:layout_height="54dp" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-8dp"
android:indeterminate="true"
style="?android:attr/progressBarStyleHorizontal" />
<TextView
android:padding="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/description_text"
style="?attr/textAppearanceListItem" />
</LinearLayout>

22
res/layout/toolbar.xml Normal file
View File

@ -0,0 +1,22 @@
<!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary" />

21
res/values/colors.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<color name="login_blue_theme_primary">#ff4285f4</color>
<color name="login_blue_theme_primary_dark">#ff3367d6</color>
<color name="login_blue_theme_accent">#ffFFAB40</color>
</resources>

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2014 μg Project Team
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2014 μg Project Team
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.

View File

@ -1,4 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<string name="gms_app_name">μg Services</string>
<string name="just_a_sec">Just a sec…</string>
<string name="google_account_type">com.google</string>
<string name="google_account_label">Google</string>
</resources>

28
res/values/themes.xml Normal file
View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="LoginBlueTheme" parent="LoginBlueTheme.Base" />
<style name="LoginBlueTheme.Base" parent="Theme.AppCompat.Light">
<item name="colorPrimary">@color/login_blue_theme_primary</item>
<item name="colorPrimaryDark">@color/login_blue_theme_primary_dark</item>
<item name="colorAccent">@color/login_blue_theme_accent</item>
<item name="android:windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<!--
<PreferenceCategory android:title="@string/google_settings_header_title" />
<PreferenceScreen android:title="@string/maps_settings_title" android:key="key1">
<intent android:action="com.google.android.apps.maps.LOCATION_SETTINGS" />
</PreferenceScreen> -->
</PreferenceScreen>

23
res/xml/authenticator.xml Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2013-2015 µg Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountPreferences="@xml/account_preferences"
android:accountType="@string/google_account_type"
android:customTokens="true"
android:icon="@drawable/auth_gls_ic_google_selected"
android:smallIcon="@drawable/auth_gls_ic_google_minitab_selected"
android:label="@string/google_account_label" />

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 μg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 μg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 μg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 μg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 μg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 μg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -0,0 +1,96 @@
/*
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth;
import android.net.Uri;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
public class AuthClient {
private static final String TAG = "GmsAuthClient";
private static final String SERVICE_URL = "https://android.clients.google.com/auth";
public static AuthResponse request(AuthRequest request) throws IOException {
AuthResponse authResponse = new AuthResponse();
HttpURLConnection connection = (HttpURLConnection) new URL(SERVICE_URL).openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
Map<String, String> httpHeaders = request.getHttpHeaders();
for (String key : httpHeaders.keySet()) {
connection.setRequestProperty(key, httpHeaders.get(key));
}
StringBuilder content = new StringBuilder();
Map<String, String> formContent = request.getFormContent();
for (String key : formContent.keySet()) {
if (content.length() > 0)
content.append("&");
content.append(Uri.encode(key)).append("=").append(Uri.encode(formContent.get(key)));
}
OutputStream os = connection.getOutputStream();
os.write(content.toString().getBytes());
os.close();
if (connection.getResponseCode() != 200) {
throw new IOException(connection.getResponseMessage());
}
String result = new String(readStreamToEnd(connection.getInputStream()));
return AuthResponse.parse(result);
}
protected static byte[] readStreamToEnd(final InputStream is) throws IOException {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (is != null) {
final byte[] buff = new byte[1024];
while (true) {
final int nb = is.read(buff);
if (nb < 0) {
break;
}
bos.write(buff, 0, nb);
}
is.close();
}
return bos.toByteArray();
}
public static void request(final AuthRequest request, final GmsAuthCallback callback) {
new Thread(new Runnable() {
@Override
public void run() {
try {
callback.onResponse(request(request));
} catch (Exception e) {
callback.onException(e);
}
}
}).start();
}
public static interface GmsAuthCallback {
void onResponse(AuthResponse response);
void onException(Exception exception);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -0,0 +1,91 @@
/*
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth;
import java.util.HashMap;
import java.util.Map;
public class AuthRequest {
private static final String USER_AGENT = "GoogleAuth/1.4 (%s %s)";
public String app;
public String appSignature;
public String caller;
public String callerSignature;
public String androidIdHex;
public String deviceName;
public String buildVersion;
public int sdkVersion;
public String countryCode;
public String operatorCountryCode;
public String locale;
public int gmsVersion;
public String accountType = "HOSTED_OR_GOOGLE";
public String email;
public String service;
public String source = "android";
public boolean isCalledFromAccountManager;
public String token;
public boolean isSystemPartition;
public boolean getAccountId;
public boolean isAccessToken;
public String droidguardResults;
public boolean hasPermission;
public boolean addAccount;
public Map<String, String> getHttpHeaders() {
Map<String, String> map = new HashMap<>();
map.put("app", app);
map.put("device", androidIdHex);
map.put("User-Agent", String.format(USER_AGENT, deviceName, buildVersion));
map.put("Content-Type", "application/x-www-form-urlencoded");
return map;
}
public Map<String, String> getFormContent() {
Map<String, String> map = new HashMap<>();
map.put("device_country", countryCode);
map.put("operatorCountry", operatorCountryCode);
map.put("lang", locale);
map.put("sdk_version", Integer.toString(sdkVersion));
map.put("google_play_services_version", Integer.toString(gmsVersion));
map.put("accountType", accountType);
if (isSystemPartition) map.put("system_partition", "1");
if (hasPermission) map.put("has_permission", "1");
if (addAccount) map.put("add_account", "1");
if (email != null) map.put("Email", email);
map.put("service", service);
map.put("source", source);
map.put("androidId", androidIdHex);
if (getAccountId) map.put("get_accountid", "1");
map.put("app", app);
map.put("client_sig", appSignature);
if (caller != null) {
map.put("callerPkg", caller);
map.put("callerSig", callerSignature);
}
if (isCalledFromAccountManager) {
map.put("is_called_from_account_manager", "1");
map.put("_opt_is_called_from_account_manager", "1");
}
if (isAccessToken) map.put("ACCESS_TOKEN", "1");
map.put("Token", token);
if (droidguardResults != null) map.put("droidguard_results", droidguardResults);
return map;
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth;
import android.util.Log;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class AuthResponse {
private static final String TAG = "GmsAuthResponse";
@ResponseField("SID")
public String Sid;
@ResponseField("LSID")
public String LSid;
@ResponseField("Auth")
public String auth;
@ResponseField("Token")
public String token;
@ResponseField("Email")
public String email;
@ResponseField("services")
public Set<String> services = new HashSet<>();
@ResponseField("GooglePlusUpgrade")
public boolean isGooglePlusUpgrade;
@ResponseField("PicasaUser")
public String picasaUserName;
@ResponseField("RopText")
public String ropText;
@ResponseField("RopRevision")
public int ropRevision;
@ResponseField("firstName")
public String firstName;
@ResponseField("lastName")
public String lastName;
@ResponseField("issueAdvice")
public String issueAdvice;
public static AuthResponse parse(String result) {
AuthResponse response = new AuthResponse();
String[] entries = result.split("\n");
for (String s : entries) {
String[] keyValuePair = s.split("=");
String key = keyValuePair[0].trim();
String value = keyValuePair[1].trim();
try {
for (Field field : AuthResponse.class.getDeclaredFields()) {
if (field.isAnnotationPresent(ResponseField.class) &&
key.equals(field.getAnnotation(ResponseField.class).value())) {
if (field.getType().equals(String.class)) {
field.set(response, value);
} else if (field.getType().equals(boolean.class)) {
field.setBoolean(response, value.equals("1"));
} else if (field.getType().equals(int.class)) {
field.setInt(response, Integer.parseInt(value));
} else if (field.getType().isAssignableFrom(Set.class)) {
//noinspection unchecked
((Set)field.get(response)).addAll(Arrays.asList(value.split(",")));
}
}
}
} catch (Exception e) {
Log.w(TAG, e);
}
}
return response;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
private @interface ResponseField {
public String value();
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth;
import java.util.Locale;
public class GmsAddAccountRequest extends AuthRequest {
public GmsAddAccountRequest(Locale locale, int sdkVersion, int gmsVersion, String deviceName,
String buildVersion, String androidIdHex, String token) {
this.service = "ac2dm";
this.addAccount = true;
this.isSystemPartition = true;
this.hasPermission = true;
this.getAccountId = true;
this.app = "com.google.android.gms";
this.appSignature = "38918a453d07199354f8b19af05ec6562ced5788";
this.androidIdHex = "3bc26ad601111308"; // TODO: androidIdHex;
this.deviceName = deviceName;
this.buildVersion = buildVersion;
this.countryCode = locale.getCountry();
this.gmsVersion = gmsVersion;
this.operatorCountryCode = locale.getCountry();
this.locale = locale.toString();
this.sdkVersion = sdkVersion;
this.token = token;
this.droidguardResults = null; // TODO
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth;
import android.content.Context;
import android.os.Build;
import org.microg.gms.Constants;
import java.util.Locale;
/**
* This request will retrieve a rt token (oauth2rt) from an access token (oauth2)
*/
public class RetrieveRtTokenRequest extends GmsAddAccountRequest {
public RetrieveRtTokenRequest(Context context, String token) {
this(Locale.getDefault(), Build.VERSION.SDK_INT, Constants.MAX_REFERENCE_VERSION,
Build.DEVICE, Build.ID, null /*TODO*/, token);
}
public RetrieveRtTokenRequest(Locale locale, int sdkVersion, int gmsVersion, String deviceName,
String buildVersion, String androidIdHex, String token) {
super(locale, sdkVersion, gmsVersion, deviceName, buildVersion, androidIdHex, token);
this.isAccessToken = true;
this.droidguardResults = null; // TODO
}
}

View File

@ -0,0 +1,224 @@
/*
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth.login;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.RelativeLayout;
import com.google.android.gms.R;
import org.microg.gms.auth.AuthClient;
import org.microg.gms.auth.AuthResponse;
import org.microg.gms.auth.GmsAddAccountRequest;
import org.microg.gms.auth.RetrieveRtTokenRequest;
import java.util.Locale;
public class LoginActivity extends Activity {
public static final String TMPL_NEW_ACCOUNT = "new_account";
public static final String EXTRA_TMPL = "tmpl";
private static final String TAG = "GmsAuthLoginBrowser";
private static final String EMBEDDED_SETUP_URL = "https://accounts.google.com/EmbeddedSetup";
private static final String MAGIC_USER_AGENT = " MinuteMaid";
private static final String COOKIE_OAUTH_TOKEN = "oauth_token";
private static final int TITLE_MIN_HEIGHT = 64;
public static final double TITLE_WIDTH_FACTOR = (8.0 / 18.0);
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_base);
formatTitle();
webView = createWebView(this);
webView.addJavascriptInterface(new JsBridge(), "mm");
((ViewGroup) findViewById(R.id.auth_root)).addView(webView);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
if ("close".equals(Uri.parse(url).getFragment()))
closeWeb();
}
});
CookieManager.getInstance().setAcceptCookie(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
load();
}
});
} else {
//noinspection deprecation
CookieManager.getInstance().removeAllCookie();
load();
}
}
private static WebView createWebView(Context context) {
WebView webView = new WebView(context);
webView.setVisibility(View.INVISIBLE);
webView.setLayoutParams(new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
webView.setBackgroundColor(Color.TRANSPARENT);
webView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return true;
}
});
prepareWebViewSettings(webView.getSettings());
return webView;
}
private static void prepareWebViewSettings(WebSettings settings) {
settings.setUserAgentString(settings.getUserAgentString() + MAGIC_USER_AGENT);
settings.setJavaScriptEnabled(true);
settings.setSupportMultipleWindows(false);
settings.setSaveFormData(false);
settings.setAllowFileAccess(false);
settings.setDatabaseEnabled(false);
settings.setNeedInitialFocus(false);
settings.setUseWideViewPort(false);
settings.setSupportZoom(false);
settings.setJavaScriptCanOpenWindowsAutomatically(false);
}
private void formatTitle() {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
double widthPixels = (double) (getResources().getDisplayMetrics().widthPixels);
findViewById(R.id.title_container).getLayoutParams().height =
(int) (dpToPx(TITLE_MIN_HEIGHT) + (TITLE_WIDTH_FACTOR * widthPixels));
} else {
findViewById(R.id.title_container).getLayoutParams().height = dpToPx(TITLE_MIN_HEIGHT);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
formatTitle();
}
private void load() {
webView.loadUrl(buildUrl(getIntent().hasExtra(EXTRA_TMPL) ? getIntent().getStringExtra(EXTRA_TMPL) : TMPL_NEW_ACCOUNT, Locale.getDefault()));
}
private void closeWeb() {
runOnUiThread(new Runnable() {
@Override
public void run() {
webView.setVisibility(View.INVISIBLE);
}
});
String cookies = CookieManager.getInstance().getCookie(EMBEDDED_SETUP_URL);
String[] temp = cookies.split(";");
for (String ar1 : temp) {
if (ar1.trim().startsWith(COOKIE_OAUTH_TOKEN + "=")) {
String[] temp1 = ar1.split("=");
sendRetrieveRtToken(temp1[1]);
}
}
// TODO: Error message
}
private void sendRetrieveRtToken(String oAuthToken) {
AuthClient.request(new RetrieveRtTokenRequest(this, oAuthToken),
new AuthClient.GmsAuthCallback() {
@Override
public void onResponse(AuthResponse response) {
AccountManager accountManager = AccountManager.get(LoginActivity.this);
Account account = new Account(response.email, "com.google");
if (accountManager.addAccountExplicitly(account, response.token, null)) {
accountManager.setAuthToken(account, "SID", response.Sid);
accountManager.setAuthToken(account, "LSID", response.LSid);
setResult(RESULT_OK);
// TODO: hand over to account setup
finish();
} else {
// TODO: Error message
Log.w(TAG, "Account NOT created!");
setResult(RESULT_CANCELED);
}
}
@Override
public void onException(Exception exception) {
Log.w(TAG, "onException: " + exception);
}
});
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
private static String buildUrl(String tmpl, Locale locale) {
return Uri.parse(EMBEDDED_SETUP_URL).buildUpon()
.appendQueryParameter("source", "android")
.appendQueryParameter("xoauth_display_name", "Android Device")
.appendQueryParameter("lang", locale.getLanguage())
.appendQueryParameter("cc", locale.getCountry().toLowerCase())
.appendQueryParameter("langCountry", locale.toString().toLowerCase())
.appendQueryParameter("hl", locale.toString().replace("_", "-"))
.appendQueryParameter("tmpl", tmpl)
.build().toString();
}
private class JsBridge {
@JavascriptInterface
public void showView() {
runOnUiThread(new Runnable() {
@Override
public void run() {
webView.setVisibility(View.VISIBLE);
}
});
}
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth.loginservice;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
public class GoogleLoginService extends Service {
@Override
public IBinder onBind(Intent intent) {
if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
return new AbstractAccountAuthenticator(this) {
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
return null;
}
}.getIBinder();
}
return null;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 µg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 μg Project Team
* Copyright 2013-2015 µg Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.