Finish ask permission dialog UI

This commit is contained in:
mar-v-in 2015-02-09 23:16:37 +01:00
parent de128ac9af
commit 95919889d0
21 changed files with 538 additions and 64 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,19 @@
<?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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/dialog_border_color" />
</shape>

View File

@ -15,46 +15,72 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:gravity="center_horizontal"
android:padding="10dp"
android:text="@string/account_manager_title"
android:textColor="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:background="@color/dialog_border_color"
android:layout_width="match_parent"
android:layout_height="1dp" />
<LinearLayout
android:padding="50dp"
android:paddingTop="15dp"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<de.hdodenhof.circleimageview.CircleImageView
android:src="@color/login_blue_theme_accent"
android:id="@+id/account_photo"
android:layout_marginRight="-5dp"
<FrameLayout
android:background="@drawable/circle_shape_background"
android:layout_marginEnd="-3dp"
android:layout_marginRight="-3dp"
android:layout_width="64dp"
android:layout_height="64dp"
app:border_width="10dp"
app:border_color="#88888888" />
android:padding="10dp">
<de.hdodenhof.circleimageview.CircleImageView
android:src="@color/login_blue_theme_primary"
android:id="@+id/app_icon"
android:layout_marginLeft="-5dp"
<de.hdodenhof.circleimageview.CircleImageView
android:src="@drawable/ic_generic_man"
android:id="@+id/account_photo"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout
android:background="@drawable/circle_shape_background"
android:layout_marginStart="-3dp"
android:layout_marginLeft="-3dp"
android:layout_width="64dp"
android:layout_height="64dp"
app:border_width="10dp"
app:border_color="#88888888" />
android:padding="10dp">
<ImageView
android:src="@drawable/ic_microg_app"
android:id="@+id/app_icon"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>
<TextView
android:id="@+id/title"
android:padding="10dp"
android:padding="15dp"
android:gravity="center_horizontal"
android:textSize="18sp"
android:text="%NAME% would like to:"
android:textColor="?attr/colorAccent"
android:text="@string/ask_permission_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ListView
android:padding="5dp"
android:id="@+id/permissions"
android:layout_weight="1"
android:layout_width="match_parent"
@ -62,23 +88,33 @@
<TextView
android:padding="10dp"
android:text="By continuing, you will allow this app and Google to use your information in accordance with their respective terms of service and privacy services."
android:textColor="?attr/colorPrimary"
android:textSize="12sp"
android:text="@string/ask_permission_tos"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_gravity="end"
android:orientation="horizontal"
android:layout_width="match_parent"
android:paddingRight="10dp"
android:paddingLeft="10dp"
android:paddingBottom="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:text="@android:string/cancel"
android:layout_weight="1"
android:layout_width="0dip"
android:id="@android:id/button2"
style="?android:attr/buttonBarButtonStyle"
android:text="@string/deny"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="@android:string/ok"
android:layout_weight="1"
android:layout_width="0dip"
android:id="@android:id/button1"
style="?android:attr/buttonBarButtonStyle"
android:text="@string/allow"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -0,0 +1,35 @@
<?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="horizontal"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:src="@drawable/proprietary_auth_ic_scope_icon_default"
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:padding="6dp"
android:textSize="16sp"
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -18,4 +18,6 @@
<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>
<color name="dialog_border_color">#88CCCCCC</color>
</resources>

View File

@ -0,0 +1,92 @@
<?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="permission_service_all_label">All Google services</string>
<string name="permission_service_all_description">Allows app to access all Google services through any associated Google account.</string>
<string name="permission_service_android_label">Android services</string>
<string name="permission_service_android_description">Allows app to access Android services through any associated Google account.</string>
<string name="permission_service_adsense_label">AdSense</string>
<string name="permission_service_adsense_description">Allows app to access AdSense through any associated Google account.</string>
<string name="permission_service_adwords_label">AdWords</string>
<string name="permission_service_adwords_description">Allows app to access AdWords through any associated Google account.</string>
<string name="permission_service_ah_label">Google App Engine</string>
<string name="permission_service_ah_description">Allows app to access Google App Engine through any associated Google account.</string>
<string name="permission_service_blogger_label">Blogger</string>
<string name="permission_service_blogger_description">Allows app to access Blogger through any associated Google account.</string>
<string name="permission_service_cl_label">Google Calendar</string>
<string name="permission_service_cl_description">Allows app to access Google Calendar through any associated Google account.</string>
<string name="permission_service_cp_label">Contacts</string>
<string name="permission_service_cp_description">Allows app to access Contacts through any associated Google account.</string>
<string name="permission_service_dodgeball_label">Dodgeball</string>
<string name="permission_service_dodgeball_description">Allows app to access Dodgeball through any associated Google account.</string>
<string name="permission_service_finance_label">Google Finance</string>
<string name="permission_service_finance_description">Allows app to access Google Finance through any associated Google account.</string>
<string name="permission_service_gbase_label">Google Base</string>
<string name="permission_service_gbase_description">Allows app to access Google Base through any associated Google account.</string>
<string name="permission_service_grandcentral_label">Google Voice</string>
<string name="permission_service_grandcentral_description">Allows app to access Google Voice through any associated Google account.</string>
<string name="permission_service_groups2_label">Google Groups</string>
<string name="permission_service_groups2_description">Allows app to access Google Groups through any associated Google account.</string>
<string name="permission_service_health_label">Google Health</string>
<string name="permission_service_health_description">Allows app to access Google Health through any associated Google account.</string>
<string name="permission_service_ig_label">iGoogle</string>
<string name="permission_service_ig_description">Allows app to access iGoogle through any associated Google account.</string>
<string name="permission_service_jotspot_label">JotSpot</string>
<string name="permission_service_jotspot_description">Allows app to access JotSpot through any associated Google account.</string>
<string name="permission_service_knol_label">Knol</string>
<string name="permission_service_knol_description">Allows app to access Knol through any associated Google account.</string>
<string name="permission_service_lh2_label">Picasa Web Albums</string>
<string name="permission_service_lh2_description">Allows app to access Picasa Web Albums through any associated Google account.</string>
<string name="permission_service_local_label">Google Maps</string>
<string name="permission_service_local_description">Allows app to access Google Maps through any associated Google account.</string>
<string name="permission_service_mail_label">Google Mail</string>
<string name="permission_service_mail_description">Allows app to access Google Mail through any associated Google account.</string>
<string name="permission_service_news_label">Google News</string>
<string name="permission_service_news_description">Allows app to access Google News through any associated Google account.</string>
<string name="permission_service_notebook_label">Google Notebook</string>
<string name="permission_service_notebook_description">Allows app to access Google Notebook through any associated Google account.</string>
<string name="permission_service_orkut_label">Orkut</string>
<string name="permission_service_orkut_description">Allows app to access Orkut through any associated Google account.</string>
<string name="permission_service_print_label">Google Book Search</string>
<string name="permission_service_print_description">Allows app to access Google Book Search through any associated Google account.</string>
<string name="permission_service_sierra_label">Google Checkout accounts</string>
<string name="permission_service_sierra_description">Allows app to access Google Checkout accounts through any associated Google account.</string>
<string name="permission_service_sierraqa_label">Google Checkout QA accounts</string>
<string name="permission_service_sierraqa_description">Allows app to access Google Checkout QA accounts through any associated Google account.</string>
<string name="permission_service_sierrasandbox_label">Google Checkout Sandbox accounts</string>
<string name="permission_service_sierrasandbox_description">Allows app to access Google Checkout Sandbox accounts through any associated Google account.</string>
<string name="permission_service_sitemaps_label">Google Webmaster Tools</string>
<string name="permission_service_sitemaps_description">Allows app to access Google Webmaster Tools through any associated Google account.</string>
<string name="permission_service_speech_label">Voice Search</string>
<string name="permission_service_speech_description">Allows app to access Voice Search through any associated Google account.</string>
<string name="permission_service_speechpersonalization_label">Personalized Speech Recognition</string>
<string name="permission_service_speechpersonalization_description">Allows app to access Personalized Speech Recognition through any associated Google account.</string>
<string name="permission_service_talk_label">Google Talk</string>
<string name="permission_service_talk_description">Allows app to access Google Talk through any associated Google account.</string>
<string name="permission_service_wifi_label">Google Wi-Fi</string>
<string name="permission_service_wifi_description">Allows app to access Google Wi-Fi through any associated Google account.</string>
<string name="permission_service_wise_label">Google Spreadsheets</string>
<string name="permission_service_wise_description">Allows app to access Google Spreadsheets through any associated Google account.</string>
<string name="permission_service_writely_label">Google Docs</string>
<string name="permission_service_writely_description">Allows app to access Google Docs through any associated Google account.</string>
<string name="permission_service_youtube_label">YouTube</string>
<string name="permission_service_youtube_description">Allows app to access YouTube through any associated Google account.</string>
<string name="permission_service_YouTubeUser_label">YouTube usernames</string>
<string name="permission_service_YouTubeUser_description">Allows app to access YouTube username(s) used with any associated Google account.</string>
</resources>

View File

@ -15,8 +15,13 @@
-->
<resources>
<string name="gms_app_name">μg Services</string>
<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>
<string name="ask_permission_tos">By continuing, you allow this app and Google to use your information in accordance with their respective terms of service and privacy policies.</string>
<string name="ask_permission_title">%s would like to:</string>
<string name="account_manager_title">µg Google Account Manager</string>
<string name="allow">Allow</string>
<string name="deny">Deny</string>
</resources>

View File

@ -18,6 +18,6 @@
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:icon="@drawable/proprietary_auth_gls_ic_google_selected"
android:smallIcon="@drawable/proprietary_auth_gls_ic_google_minitab_selected"
android:label="@string/google_account_label" />

View File

@ -16,15 +16,147 @@
package org.microg.gms.auth;
import android.accounts.Account;
import android.app.Activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.google.android.gms.R;
import org.microg.gms.userinfo.ProfileManager;
public class AskPermissionActivity extends Activity {
private static final String TAG = "GmsAuthAskPermission";
private Account account;
private String packageName;
private String service;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ask_permission);
// This makes the dialog take up the full width
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
getWindow().setAttributes(lp);
// receive package info
PackageManager packageManager = getPackageManager();
ApplicationInfo applicationInfo;
try {
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, e);
finish();
return;
}
CharSequence appLabel = packageManager.getApplicationLabel(applicationInfo);
Drawable appIcon = packageManager.getApplicationIcon(applicationInfo);
Bitmap profileIcon = ProfileManager.getProfilePicture(this, account, false);
if (profileIcon != null) {
((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon);
} else {
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap profileIcon = ProfileManager.getProfilePicture(AskPermissionActivity.this, account, true);
runOnUiThread(new Runnable() {
@Override
public void run() {
((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon);
}
});
}
}).start();
}
((ImageView) findViewById(R.id.app_icon)).setImageDrawable(appIcon);
((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_permission_title, appLabel));
findViewById(android.R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAllow();
}
});
findViewById(android.R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onDeny();
}
});
((ListView) findViewById(R.id.permissions)).setAdapter(new PermissionAdapter());
}
public void onAllow() {
AuthManager.storePermission(this, account, packageName, service);
finish();
}
public void onDeny() {
finish();
}
private class PermissionAdapter extends BaseAdapter {
private boolean isOAuth() {
return service.startsWith("oauth2:") || service.startsWith("oauth:");
}
@Override
public int getCount() {
if (isOAuth()) {
return service.split(" ").length;
}
return 1;
}
@Override
public String getItem(int position) {
if (isOAuth()) {
String tokens = service.split(":", 2)[1];
return tokens.split(" ")[position];
}
return service;
}
@Override
public long getItemId(int position) {
return getItem(position).hashCode();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
String item = getItem(position);
String label = "unknown";
if (!isOAuth()) {
int stringId = getResources().getIdentifier("permission_service_" + item + "_label", "string", getPackageName());
if (stringId != 0) {
label = getString(stringId);
}
}
View view = convertView;
if (view == null) {
view = LayoutInflater.from(AskPermissionActivity.this).inflate(R.layout.ask_permission_list_entry, null);
}
((TextView)view.findViewById(android.R.id.text1)).setText(label);
return view;
}
}
}

View File

@ -16,16 +16,12 @@
package org.microg.gms.auth;
import android.accounts.Account;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import org.microg.gms.auth.loginservice.GoogleLoginService;
import org.microg.gms.common.Utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
@ -36,7 +32,6 @@ public class AuthClient {
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);
@ -60,27 +55,11 @@ public class AuthClient {
if (connection.getResponseCode() != 200) {
throw new IOException(connection.getResponseMessage());
}
String result = new String(readStreamToEnd(connection.getInputStream()));
String result = new String(Utils.readStreamToEnd(connection.getInputStream()));
Log.d(TAG, "-- Response --\n" + result);
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

View File

@ -21,6 +21,8 @@ import android.accounts.AccountManager;
import android.content.Context;
import android.util.Log;
import org.microg.gms.common.Utils;
public class AuthManager {
private static final String TAG = "GmsAuthManager";
@ -36,11 +38,10 @@ public class AuthManager {
accountManager.setAuthToken(account, buildTokenKey(packageName, sig, "LSID"), response.LSid);
if (response.expiry > 0)
accountManager.setUserData(account, buildExpireKey(packageName, sig, service), Long.toString(response.expiry));
if (response.auth != null && response.expiry != 0) {
if (response.auth != null && response.expiry != 0 && response.storeConsentRemotely) {
accountManager.setAuthToken(account, buildTokenKey(packageName, sig, service), response.auth);
accountManager.setUserData(account, buildPermKey(packageName, sig, service), "1");
}
}
public static boolean isPermitted(Context context, Account account, String packageName,
@ -60,6 +61,10 @@ public class AuthManager {
return true;
}
public static void storePermission(Context context, Account account, String packageName, String service) {
storePermission(context, account, packageName, Utils.getFirstPackageSignatureDigest(context, packageName), service);
}
public static void storePermission(Context context, Account account, String packageName,
String sig, String service) {
AccountManager accountManager = AccountManager.get(context);

View File

@ -61,7 +61,7 @@ public class AuthResponse {
@ResponseField("Expiry")
public long expiry = -1;
@ResponseField("storeConsentRemotely")
public boolean storeConsentRemotely;
public boolean storeConsentRemotely = true;
public static AuthResponse parse(String result) {
AuthResponse response = new AuthResponse();

View File

@ -43,6 +43,7 @@ import org.microg.gms.auth.AuthRequest;
import org.microg.gms.auth.AuthResponse;
import org.microg.gms.common.Constants;
import org.microg.gms.common.Utils;
import org.microg.gms.userinfo.ProfileManager;
import java.util.Locale;
@ -218,21 +219,14 @@ public class LoginActivity extends AssistantActivity {
}
private void retrieveGmsKeyUserinfoProfile(final Account account) {
final String service = "oauth2:https://www.googleapis.com/auth/userinfo.profile";
new AuthRequest().fromContext(this)
.appIsGms().callerIsGms()
.service(service)
.email(account.name)
.token(AccountManager.get(this).getPassword(account))
.systemPartition()
.hasPermission()
.getAccountId()
ProfileManager.getAuthKeyRequest(this, account)
.getResponseAsync(new AuthClient.GmsAuthCallback() {
@Override
public void onResponse(AuthResponse response) {
AuthManager.storeResponse(LoginActivity.this, account,
Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1,
service, response);
ProfileManager.SERVICE_TOKEN, response);
ProfileManager.storeAuthKey(LoginActivity.this, account, response.auth);
finish();
}

View File

@ -21,6 +21,9 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@ -39,7 +42,7 @@ public class Utils {
return new Build();
}
public static void checkPackage(Context context, String packageName,int callingUid) {
public static void checkPackage(Context context, String packageName, int callingUid) {
String[] packagesForUid = context.getPackageManager().getPackagesForUid(callingUid);
if (packagesForUid != null && !Arrays.asList(packagesForUid).contains(packageName)) {
throw new SecurityException("callingUid [" + callingUid + "] is not related to packageName [" + packageName + "]");
@ -91,4 +94,20 @@ public class Utils {
}
return null;
}
public 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();
}
}

View File

@ -0,0 +1,156 @@
/*
* 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.userinfo;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.microg.gms.auth.AuthManager;
import org.microg.gms.auth.AuthRequest;
import org.microg.gms.auth.AuthResponse;
import org.microg.gms.common.Constants;
import org.microg.gms.common.Utils;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
public class ProfileManager {
private static final String TAG = "GmsProfileManager";
private static final String PREFERENCES_NAME = "profile_manager";
public static final String SERVICE_TOKEN = "oauth2:https://www.googleapis.com/auth/userinfo.profile";
public static class ProfileInfo {
public final String familyName;
public final String givenName;
public final long id;
public final String link;
public final String locale;
public final String name;
public final String picture;
public ProfileInfo(String familyName, String givenName, long id, String link, String locale, String name, String picture) {
this.familyName = familyName;
this.givenName = givenName;
this.id = id;
this.link = link;
this.locale = locale;
this.name = name;
this.picture = picture;
}
public static ProfileInfo parse(byte[] bytes) throws JSONException {
return parse(new String(bytes));
}
private static ProfileInfo parse(String info) throws JSONException {
return parse(new JSONObject(info));
}
private static ProfileInfo parse(JSONObject info) throws JSONException {
return new ProfileInfo(
info.has("family_name") ? info.getString("family_name") : null,
info.has("given_name") ? info.getString("given_name") : null,
info.has("id") ? info.getLong("id") : 0,
info.has("link") ? info.getString("link") : null,
info.has("locale") ? info.getString("locale") : null,
info.has("name") ? info.getString("name") : null,
info.has("picture") ? info.getString("picture") : null);
}
}
public static SharedPreferences getPreferences(Context context) {
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
}
public static Bitmap getProfilePicture(Context context, Account account, boolean network) {
SharedPreferences preferences = getPreferences(context);
String picture = preferences.getString("profile_picture", null);
if (picture != null) {
byte[] bytes = Base64.decode(picture, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}
if (!network) return null;
try {
URLConnection conn = new URL(getProfileInfo(context, account).picture).openConnection();
conn.setDoInput(true);
byte[] bytes = Utils.readStreamToEnd(conn.getInputStream());
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap != null)
preferences.edit().putString("profile_picture", Base64.encodeToString(bytes, Base64.DEFAULT)).commit();
return bitmap;
} catch (Exception e) {
Log.w(TAG, e);
return null;
}
}
public static ProfileInfo getProfileInfo(Context context, Account account) {
try {
URLConnection conn = new URL("https://www.googleapis.com/oauth2/v1/userinfo").openConnection();
conn.addRequestProperty("Authorization", "Bearer " + getAuthKey(context, account));
conn.setDoInput(true);
byte[] bytes = Utils.readStreamToEnd(conn.getInputStream());
return ProfileInfo.parse(bytes);
} catch (JSONException | IOException e) {
Log.w(TAG, e);
return null;
}
}
public static AuthRequest getAuthKeyRequest(Context context, Account account) {
return new AuthRequest().fromContext(context)
.appIsGms().callerIsGms()
.service(SERVICE_TOKEN)
.email(account.name)
.token(AccountManager.get(context).getPassword(account))
.systemPartition()
.hasPermission()
.getAccountId();
}
public static String getAuthKey(Context context, Account account) {
String result = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
.getString(account.name + "_auth_key", null);
if (result == null) {
try {
AuthResponse response = getAuthKeyRequest(context, account).getResponse();
AuthManager.storeResponse(context, account,
Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1,
SERVICE_TOKEN, response);
result = response.auth;
storeAuthKey(context, account, result);
} catch (IOException e) {
return null;
}
}
return result;
}
public static void storeAuthKey(Context context, Account account, String key) {
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit()
.putString(account.name + "_auth_key", key).commit();
}
}