Merge "Added account avatar in the end of search bar"
This commit is contained in:
@@ -18,8 +18,10 @@ package com.android.settings.accounts;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public interface AccountFeatureProvider {
|
||||
String getAccountType();
|
||||
Account[] getAccounts(Context context);
|
||||
Intent getAccountSettingsDeeplinkIntent();
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package com.android.settings.accounts;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class AccountFeatureProviderImpl implements AccountFeatureProvider {
|
||||
@Override
|
||||
@@ -13,4 +14,9 @@ public class AccountFeatureProviderImpl implements AccountFeatureProvider {
|
||||
public Account[] getAccounts(Context context) {
|
||||
return new Account[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getAccountSettingsDeeplinkIntent() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -17,18 +17,30 @@
|
||||
package com.android.settings.accounts;
|
||||
|
||||
import android.accounts.Account;
|
||||
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.utils.ThreadUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Avatar related work to the onStart method of registered observable classes
|
||||
@@ -37,12 +49,39 @@ import com.android.settings.overlay.FeatureFactory;
|
||||
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 int REQUEST_CODE = 1013;
|
||||
|
||||
private final Context mContext;
|
||||
private final ImageView mAvatarView;
|
||||
private final MutableLiveData<Bitmap> mAvatarImage;
|
||||
|
||||
public AvatarViewMixin(Context context, ImageView avatarView) {
|
||||
mContext = context.getApplicationContext();
|
||||
public AvatarViewMixin(SettingsHomepageActivity activity, ImageView avatarView) {
|
||||
mContext = activity.getApplicationContext();
|
||||
mAvatarView = avatarView;
|
||||
mAvatarView.setOnClickListener(v -> {
|
||||
if (hasAccount()) {
|
||||
//TODO(b/117509285) launch the new page of the MeCard
|
||||
} else {
|
||||
final Intent intent = FeatureFactory.getFactory(mContext)
|
||||
.getAccountFeatureProvider()
|
||||
.getAccountSettingsDeeplinkIntent();
|
||||
|
||||
if (intent != null) {
|
||||
activity.startActivityForResult(intent, REQUEST_CODE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mAvatarImage = new MutableLiveData<>();
|
||||
mAvatarImage.observe(activity, bitmap -> {
|
||||
avatarView.setImageBitmap(bitmap);
|
||||
});
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
||||
@@ -52,7 +91,7 @@ public class AvatarViewMixin implements LifecycleObserver {
|
||||
return;
|
||||
}
|
||||
if (hasAccount()) {
|
||||
//TODO(b/117509285): To migrate account icon on search bar
|
||||
loadAvatar();
|
||||
} else {
|
||||
mAvatarView.setImageResource(R.drawable.ic_account_circle_24dp);
|
||||
}
|
||||
@@ -64,4 +103,34 @@ public class AvatarViewMixin implements LifecycleObserver {
|
||||
mContext).getAccountFeatureProvider().getAccounts(mContext);
|
||||
return (accounts != null) && (accounts.length > 0);
|
||||
}
|
||||
|
||||
private void loadAvatar() {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,10 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.settings.homepage.SettingsHomepageActivity;
|
||||
@@ -39,38 +43,45 @@ import org.robolectric.android.controller.ActivityController;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
import org.robolectric.shadows.ShadowPackageManager;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class AvatarViewMixinTest {
|
||||
private static final String DUMMY_ACCOUNT = "test@domain.com";
|
||||
private static final String DUMMY_DOMAIN = "domain.com";
|
||||
private static final String DUMMY_AUTHORITY = "authority.domain.com";
|
||||
|
||||
private Context mContext;
|
||||
private ImageView mImageView;
|
||||
private ActivityController mController;
|
||||
private SettingsHomepageActivity mActivity;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mImageView = new ImageView(mContext);
|
||||
mController = Robolectric.buildActivity(SettingsHomepageActivity.class).create();
|
||||
mActivity = (SettingsHomepageActivity) mController.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAccount_useDefaultAccountData_returnFalse() {
|
||||
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mContext, mImageView);
|
||||
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
|
||||
assertThat(avatarViewMixin.hasAccount()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowAccountFeatureProviderImpl.class)
|
||||
public void hasAccount_useShadowAccountData_returnTrue() {
|
||||
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mContext, mImageView);
|
||||
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
|
||||
assertThat(avatarViewMixin.hasAccount()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_configDisabled_doNothing() {
|
||||
final AvatarViewMixin mixin = spy(new AvatarViewMixin(mContext, mImageView));
|
||||
final AvatarViewMixin mixin = spy(new AvatarViewMixin(mActivity, mImageView));
|
||||
mixin.onStart();
|
||||
|
||||
verify(mixin, never()).hasAccount();
|
||||
@@ -79,19 +90,45 @@ public class AvatarViewMixinTest {
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void onStart_useMockAvatarViewMixin_shouldBeExecuted() {
|
||||
final AvatarViewMixin mockAvatar = spy(new AvatarViewMixin(mContext, mImageView));
|
||||
final AvatarViewMixin mockAvatar = spy(new AvatarViewMixin(mActivity, mImageView));
|
||||
|
||||
final ActivityController controller = Robolectric.buildActivity(
|
||||
SettingsHomepageActivity.class).create();
|
||||
final SettingsHomepageActivity settingsHomepageActivity =
|
||||
(SettingsHomepageActivity) controller.get();
|
||||
settingsHomepageActivity.getLifecycle().addObserver(mockAvatar);
|
||||
controller.start();
|
||||
mActivity.getLifecycle().addObserver(mockAvatar);
|
||||
mController.start();
|
||||
|
||||
verify(mockAvatar).hasAccount();
|
||||
}
|
||||
|
||||
@Implements(AccountFeatureProviderImpl.class)
|
||||
@Test
|
||||
public void queryProviderAuthority_useShadowPackagteManager_returnNull() {
|
||||
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
|
||||
|
||||
assertThat(avatarViewMixin.queryProviderAuthority()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryProviderAuthority_useNewShadowPackagteManager_returnAuthority() {
|
||||
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
|
||||
ShadowPackageManager shadowPackageManager = Shadow.extract(mContext.getPackageManager());
|
||||
final PackageInfo accountProvider = new PackageInfo();
|
||||
accountProvider.packageName = "test.pkg";
|
||||
accountProvider.applicationInfo = new ApplicationInfo();
|
||||
accountProvider.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
|
||||
accountProvider.applicationInfo.packageName = accountProvider.packageName;
|
||||
accountProvider.providers = new ProviderInfo[1];
|
||||
accountProvider.providers[0] = new ProviderInfo();
|
||||
accountProvider.providers[0].authority = DUMMY_AUTHORITY;
|
||||
accountProvider.providers[0].packageName = accountProvider.packageName;
|
||||
accountProvider.providers[0].name = "test.class";
|
||||
accountProvider.providers[0].applicationInfo = accountProvider.applicationInfo;
|
||||
|
||||
final ResolveInfo resolveInfo = new ResolveInfo();
|
||||
resolveInfo.providerInfo = accountProvider.providers[0];
|
||||
shadowPackageManager.addResolveInfoForIntent(AvatarViewMixin.INTENT_GET_ACCOUNT_DATA,
|
||||
resolveInfo);
|
||||
assertThat(avatarViewMixin.queryProviderAuthority()).isEqualTo(DUMMY_AUTHORITY);
|
||||
}
|
||||
|
||||
@Implements(value = AccountFeatureProviderImpl.class)
|
||||
public static class ShadowAccountFeatureProviderImpl {
|
||||
|
||||
@Implementation
|
||||
|
Reference in New Issue
Block a user