blob: 9e762c725748e96a1d75e52dd7cbd66d9fe00b77 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* 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 com.android.settings.accounts;
import android.accounts.Account;
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.widget.ImageView;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.OnLifecycleEvent;
import com.android.settings.R;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;
import java.net.URISyntaxException;
import java.util.List;
/**
* Avatar related work to the onStart method of registered observable classes
* in {@link SettingsHomepageActivity}.
*/
public class AvatarViewMixin implements LifecycleObserver {
private static final String TAG = "AvatarViewMixin";
@VisibleForTesting
static final Intent INTENT_GET_ACCOUNT_DATA =
new Intent("android.content.action.SETTINGS_ACCOUNT_DATA");
private static final String METHOD_GET_ACCOUNT_AVATAR = "getAccountAvatar";
private static final String KEY_AVATAR_BITMAP = "account_avatar";
private static final String KEY_ACCOUNT_NAME = "account_name";
private static final String EXTRA_ACCOUNT_NAME = "extra.accountName";
private final Context mContext;
private final ImageView mAvatarView;
private final MutableLiveData<Bitmap> mAvatarImage;
private final ActivityManager mActivityManager;
private String mAccountName;
public AvatarViewMixin(SettingsHomepageActivity activity, ImageView avatarView) {
mContext = activity.getApplicationContext();
mActivityManager = mContext.getSystemService(ActivityManager.class);
mAvatarView = avatarView;
mAvatarView.setOnClickListener(v -> {
Intent intent;
try {
final String uri = mContext.getResources().getString(
R.string.config_account_intent_uri);
intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException e) {
Log.w(TAG, "Error parsing avatar mixin intent, skipping", e);
return;
}
if (!TextUtils.isEmpty(mAccountName)) {
intent.putExtra(EXTRA_ACCOUNT_NAME, mAccountName);
}
final List<ResolveInfo> matchedIntents =
mContext.getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_SYSTEM_ONLY);
if (matchedIntents.isEmpty()) {
Log.w(TAG, "Cannot find any matching action VIEW_ACCOUNT intent.");
return;
}
final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory.getFactory(
mContext).getMetricsFeatureProvider();
metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.CLICK_ACCOUNT_AVATAR, SettingsEnums.SETTINGS_HOMEPAGE,
null /* key */, Integer.MIN_VALUE /* value */);
// Here may have two different UI while start the activity.
// It will display adding account UI when device has no any account.
// It will display account information page when intent added the specified account.
activity.startActivity(intent);
});
mAvatarImage = new MutableLiveData<>();
mAvatarImage.observe(activity, bitmap -> {
avatarView.setImageBitmap(bitmap);
});
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
if (!mContext.getResources().getBoolean(R.bool.config_show_avatar_in_homepage)) {
Log.d(TAG, "Feature disabled by config. Skipping");
return;
}
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Feature disabled on low ram device. Skipping");
return;
}
if (hasAccount()) {
loadAccount();
} else {
mAvatarView.setImageResource(R.drawable.ic_account_circle_24dp);
}
}
@VisibleForTesting
boolean hasAccount() {
final Account accounts[] = FeatureFactory.getFactory(
mContext).getAccountFeatureProvider().getAccounts(mContext);
return (accounts != null) && (accounts.length > 0);
}
private void loadAccount() {
final String authority = queryProviderAuthority();
if (TextUtils.isEmpty(authority)) {
return;
}
ThreadUtils.postOnBackgroundThread(() -> {
final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority)
.build();
final Bundle bundle = mContext.getContentResolver().call(uri,
METHOD_GET_ACCOUNT_AVATAR, null /* arg */, null /* extras */);
final Bitmap bitmap = bundle.getParcelable(KEY_AVATAR_BITMAP);
mAccountName = bundle.getString(KEY_ACCOUNT_NAME, "" /* defaultValue */);
mAvatarImage.postValue(bitmap);
});
}
@VisibleForTesting
String queryProviderAuthority() {
final List<ResolveInfo> providers =
mContext.getPackageManager().queryIntentContentProviders(INTENT_GET_ACCOUNT_DATA,
PackageManager.MATCH_SYSTEM_ONLY);
if (providers.size() == 1) {
return providers.get(0).providerInfo.authority;
} else {
Log.w(TAG, "The size of the provider is " + providers.size());
return null;
}
}
}