UI Refresh: a new app info header for app/notification.

- moved force stop and uninstall button to bottom of page (as footer)
- Forked appheader layout file, and created AppHeaderController to
  contain all binding logic for header.

Bug: 32442716
Test: RunSettingsRoboTests
Change-Id: Id4eb365ca25e035c043c068867f5cbc3a202b201
This commit is contained in:
Fan Zhang
2016-10-26 15:43:01 -07:00
parent 9895344be6
commit c9536b8e41
14 changed files with 665 additions and 10 deletions

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:paddingTop="4dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:orientation="horizontal">
<Button
android:id="@+id/left_button"
style="@style/AppActionPrimaryButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingEnd="8dp"/>
<Button
android:id="@+id/right_button"
style="@style/AppActionPrimaryButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingStart="8dp"/>
</LinearLayout>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_snippet"
style="@style/AppHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<!-- App snippet with buttons -->
<ImageView
android:id="@android:id/icon"
android:layout_marginTop="16dp"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="fitXY"
android:layout_gravity="center_horizontal"
android:antialias="true"/>
<TextView
android:id="@android:id/title"
style="@style/TextAppearance.AppHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
android:gravity="center_horizontal"
android:paddingTop="8dp"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary"/>
<LinearLayout
android:id="@+id/app_detail_links"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:orientation="horizontal">
<ImageButton
android:id="@+id/left_button"
style="@style/AppHeaderLinkButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingEnd="8dp"
android:tint="?android:attr/colorAccent"/>
<ImageButton
android:id="@+id/right_button"
style="@style/AppHeaderLinkButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingStart="8dp"
android:src="@drawable/ic_settings_24dp"
android:tint="?android:attr/colorAccent"/>
</LinearLayout>
</LinearLayout>

View File

@@ -452,4 +452,22 @@
<item name="android:progressDrawable">@drawable/ring_progress</item>
</style>
<style name="AppHeader">
<item name="android:background">@color/card_background_grey</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:paddingStart">16dp</item>
<item name="android:paddingEnd">16dp</item>
<item name="android:paddingBottom">16dp</item>
</style>
<style name="TextAppearance.AppHeaderTitle"
parent="@android:style/TextAppearance.Material.Subhead">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">24sp</item>
</style>
<style name="AppHeaderLinkButton" parent="android:Widget.Material.Button.Borderless"/>
<style name="AppActionPrimaryButton" parent="android:Widget.Material.Button.Colored"/>
</resources>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<com.android.settings.applications.LayoutPreference
android:key="header_view"
android:layout="@layout/app_details"
android:selectable="false"/>
<Preference
android:key="storage_settings"
android:title="@string/storage_settings"
android:selectable="true"/>
<Preference
android:key="data_settings"
android:title="@string/data_usage_summary_title"
android:selectable="true"/>
<Preference
android:key="permission_settings"
android:title="@string/permissions_label"
android:selectable="true"/>
<Preference
android:key="notification_settings"
android:title="@string/notifications_label"
android:selectable="true"/>
<Preference
android:key="preferred_settings"
android:title="@string/launch_by_default"
android:selectable="true"/>
<Preference
android:key="battery"
android:title="@string/power_usage_summary_title"
android:selectable="true"/>
<Preference
android:key="memory"
android:title="@string/memory_settings_title"
android:enabled="false"
android:selectable="true"/>
</PreferenceScreen>

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2016 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.applications;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.support.annotation.IntDef;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.applications.ApplicationsState;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class AppHeaderController {
@IntDef({ActionType.ACTION_APP_INFO,
ActionType.ACTION_APP_PREFERENCE,
ActionType.ACTION_STORE_DEEP_LINK,
ActionType.ACTION_NOTIF_PREFERENCE})
@Retention(RetentionPolicy.SOURCE)
@interface ActionType {
int ACTION_APP_INFO = 0;
int ACTION_STORE_DEEP_LINK = 1;
int ACTION_APP_PREFERENCE = 2;
int ACTION_NOTIF_PREFERENCE = 3;
}
private static final String TAG = "AppDetailFeature";
private final Context mContext;
public AppHeaderController(Context context) {
mContext = context;
}
public void bindAppHeader(View appSnippet, PackageInfo packageInfo,
ApplicationsState.AppEntry appEntry) {
final String versionName = packageInfo == null ? null : packageInfo.versionName;
final Resources res = appSnippet.getResources();
// Set Icon
final ImageView iconView = (ImageView) appSnippet.findViewById(android.R.id.icon);
if (appEntry.icon != null) {
iconView.setImageDrawable(appEntry.icon.getConstantState().newDrawable(res));
}
// Set application name.
final TextView labelView = (TextView) appSnippet.findViewById(android.R.id.title);
labelView.setText(appEntry.label);
// Version number of application
final TextView appVersion = (TextView) appSnippet.findViewById(android.R.id.summary);
if (!TextUtils.isEmpty(versionName)) {
appVersion.setSelected(true);
appVersion.setVisibility(View.VISIBLE);
appVersion.setText(res.getString(R.string.version_text, String.valueOf(versionName)));
} else {
appVersion.setVisibility(View.INVISIBLE);
}
}
public void bindAppHeaderButtons(Fragment fragment, View appLinkButtons, String packageName,
@ActionType int leftAction, @ActionType int rightAction) {
ImageButton leftButton = (ImageButton) appLinkButtons.findViewById(R.id.left_button);
ImageButton rightButton = (ImageButton) appLinkButtons.findViewById(R.id.right_button);
bindAppDetailButton(fragment, packageName, leftButton, leftAction);
bindAppDetailButton(fragment, packageName, rightButton, rightAction);
}
private void bindAppDetailButton(Fragment fragment, String packageName,
ImageButton button, @ActionType int action) {
if (button == null) {
return;
}
switch (action) {
case ActionType.ACTION_APP_INFO: {
if (packageName == null || packageName.equals(Utils.OS_PKG)) {
button.setVisibility(View.GONE);
} else {
// TODO
button.setImageResource(com.android.settings.R.drawable.ic_info);
button.setVisibility(View.VISIBLE);
}
return;
}
case ActionType.ACTION_STORE_DEEP_LINK: {
final Intent intent = new Intent(Intent.ACTION_SHOW_APP_INFO)
.setPackage(getInstallerPackageName(mContext, packageName));
final Intent result = resolveIntent(intent);
if (result == null) {
button.setVisibility(View.GONE);
} else {
result.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
button.setImageResource(R.drawable.ic_sim_sd);
button.setOnClickListener(v -> fragment.startActivity(intent));
button.setVisibility(View.VISIBLE);
}
return;
}
case ActionType.ACTION_NOTIF_PREFERENCE: {
// TODO
return;
}
case ActionType.ACTION_APP_PREFERENCE: {
final Intent intent = resolveIntent(
new Intent(Intent.ACTION_APPLICATION_PREFERENCES).setPackage(packageName));
if (intent == null) {
button.setVisibility(View.GONE);
return;
}
button.setOnClickListener(v -> fragment.startActivity(intent));
button.setVisibility(View.VISIBLE);
return;
}
}
}
private String getInstallerPackageName(Context context, String packageName) {
try {
return context.getPackageManager().getInstallerPackageName(packageName);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Exception while retrieving the package installer of " + packageName, e);
return null;
}
}
private Intent resolveIntent(Intent i) {
ResolveInfo result = mContext.getPackageManager().resolveActivity(i, 0);
if (result != null) {
return new Intent(i.getAction())
.setClassName(result.activityInfo.packageName, result.activityInfo.name);
}
return null;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016 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.applications;
public interface ApplicationFeatureProvider {
AppHeaderController getAppHeaderController();
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2016 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.applications;
import android.content.Context;
public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvider {
private final Context mContext;
private AppHeaderController mAppHeaderController;
public ApplicationFeatureProviderImpl(Context context) {
mContext = context.getApplicationContext();
}
@Override
public AppHeaderController getAppHeaderController() {
if (mAppHeaderController == null) {
mAppHeaderController = new AppHeaderController(mContext);
}
return mAppHeaderController;
}
}

View File

@@ -84,6 +84,7 @@ import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.datausage.AppDataUsage;
import com.android.settings.datausage.DataUsageList;
import com.android.settings.datausage.DataUsageSummary;
@@ -92,6 +93,7 @@ import com.android.settings.fuelgauge.PowerUsageDetail;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.AppUtils;
@@ -138,6 +140,7 @@ public class InstalledAppDetails extends AppInfoBase
private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
private static final String KEY_HEADER = "header_view";
private static final String KEY_FOOTER = "header_footer";
private static final String KEY_NOTIFICATION = "notification_settings";
private static final String KEY_STORAGE = "storage_settings";
private static final String KEY_PERMISSION = "permission_settings";
@@ -148,11 +151,15 @@ public class InstalledAppDetails extends AppInfoBase
private static final String NOTIFICATION_TUNER_SETTING = "show_importance_slider";
private final HashSet<String> mHomePackages = new HashSet<String>();
private final HashSet<String> mHomePackages = new HashSet<>();
private DashboardFeatureProvider mDashboardFeatureProvider;
private AppHeaderController mAppHeaderController;
private boolean mInitialized;
private boolean mShowUninstalled;
private LayoutPreference mHeader;
private LayoutPreference mFooter;
private Button mUninstallButton;
private boolean mUpdatedSysApp = false;
private Button mForceStopButton;
@@ -164,6 +171,7 @@ public class InstalledAppDetails extends AppInfoBase
private Preference mMemoryPreference;
private boolean mDisableAfterUninstall;
// Used for updating notification preference.
private final NotificationBackend mBackend = new NotificationBackend();
@@ -306,9 +314,16 @@ public class InstalledAppDetails extends AppInfoBase
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Activity activity = getActivity();
mDashboardFeatureProvider =
FeatureFactory.getFactory(activity).getDashboardFeatureProvider(activity);
mAppHeaderController = FeatureFactory.getFactory(activity)
.getApplicationFeatureProvider(activity).getAppHeaderController();
setHasOptionsMenu(true);
addPreferencesFromResource(R.xml.installed_app_details);
addPreferencesFromResource(mDashboardFeatureProvider.isEnabled()
? R.xml.installed_app_details_ia
: R.xml.installed_app_details);
addDynamicPrefs();
if (Utils.isBandwidthControlEnabled()) {
@@ -366,7 +381,18 @@ public class InstalledAppDetails extends AppInfoBase
if (mFinishing) {
return;
}
if (!mDashboardFeatureProvider.isEnabled()) {
handleHeader();
} else {
mHeader = (LayoutPreference) findPreference(KEY_HEADER);
mAppHeaderController.bindAppHeaderButtons(
this,
mHeader.findViewById(R.id.app_detail_links),
mPackageName,
AppHeaderController.ActionType.ACTION_STORE_DEEP_LINK,
AppHeaderController.ActionType.ACTION_APP_PREFERENCE);
prepareUninstallAndStop();
}
mNotificationPreference = findPreference(KEY_NOTIFICATION);
mNotificationPreference.setOnPreferenceClickListener(this);
@@ -404,7 +430,6 @@ public class InstalledAppDetails extends AppInfoBase
private void handleHeader() {
mHeader = (LayoutPreference) findPreference(KEY_HEADER);
// Get Control button panel
View btnPanel = mHeader.findViewById(R.id.control_buttons_panel);
mForceStopButton = (Button) btnPanel.findViewById(R.id.right_button);
@@ -429,6 +454,13 @@ public class InstalledAppDetails extends AppInfoBase
}
}
private void prepareUninstallAndStop() {
mForceStopButton = (Button) mFooter.findViewById(R.id.right_button);
mForceStopButton.setText(R.string.force_stop);
mUninstallButton = (Button) mFooter.findViewById(R.id.left_button);
mForceStopButton.setEnabled(false);
}
private Intent resolveIntent(Intent i) {
ResolveInfo result = getContext().getPackageManager().resolveActivity(i, 0);
return result != null ? new Intent(i.getAction())
@@ -511,9 +543,13 @@ public class InstalledAppDetails extends AppInfoBase
private void setAppLabelAndIcon(PackageInfo pkgInfo) {
final View appSnippet = mHeader.findViewById(R.id.app_snippet);
mState.ensureIcon(mAppEntry);
if (mDashboardFeatureProvider.isEnabled()) {
mAppHeaderController.bindAppHeader(appSnippet, pkgInfo, mAppEntry);
} else {
setupAppSnippet(appSnippet, mAppEntry.label, mAppEntry.icon,
pkgInfo != null ? pkgInfo.versionName : null);
}
}
private boolean signaturesMatch(String pkg1, String pkg2) {
if (pkg1 != null && pkg2 != null) {
@@ -917,6 +953,12 @@ public class InstalledAppDetails extends AppInfoBase
}
addAppInstallerInfoPref(screen);
if (mDashboardFeatureProvider.isEnabled()) {
mFooter = new LayoutPreference(screen.getContext(), R.layout.app_action_buttons);
mFooter.setOrder(10000);
mFooter.setKey(KEY_FOOTER);
screen.addPreference(mFooter);
}
}
private void addAppInstallerInfoPref(PreferenceScreen screen) {

View File

@@ -38,8 +38,8 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
private final CategoryManager mCategoryManager;
public DashboardFeatureProviderImpl(Context context) {
mContext = context;
mCategoryManager = CategoryManager.get(context);
mContext = context.getApplicationContext();
mCategoryManager = CategoryManager.get(mContext);
}
@Override

View File

@@ -21,6 +21,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -71,6 +72,8 @@ public abstract class FeatureFactory {
public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context);
public abstract ApplicationFeatureProvider getApplicationFeatureProvider(Context context);
public abstract LocaleFeatureProvider getLocaleFeatureProvider();

View File

@@ -19,6 +19,8 @@ package com.android.settings.overlay;
import android.content.Context;
import android.support.annotation.Keep;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProviderImpl;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.core.instrumentation.MetricsFeatureProviderImpl;
import com.android.settings.dashboard.DashboardFeatureProvider;
@@ -33,6 +35,7 @@ import com.android.settings.localepicker.LocaleFeatureProviderImpl;
@Keep
public final class FeatureFactoryImpl extends FeatureFactory {
private ApplicationFeatureProvider mApplicationFeatureProvider;
private MetricsFeatureProvider mMetricsFeatureProvider;
private DashboardFeatureProviderImpl mDashboardFeatureProvider;
private LocaleFeatureProvider mLocaleFeatureProvider;
@@ -63,6 +66,14 @@ public final class FeatureFactoryImpl extends FeatureFactory {
return mDashboardFeatureProvider;
}
@Override
public ApplicationFeatureProvider getApplicationFeatureProvider(Context context) {
if (mApplicationFeatureProvider == null) {
mApplicationFeatureProvider = new ApplicationFeatureProviderImpl(context);
}
return mApplicationFeatureProvider;
}
@Override
public LocaleFeatureProvider getLocaleFeatureProvider() {
if (mLocaleFeatureProvider == null) {

View File

@@ -19,6 +19,7 @@ package com.android.settings.applications;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -28,14 +29,13 @@ import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class AdvancedAppSettingsTest {

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2016 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.applications;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class AppHeaderControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ApplicationsState.AppEntry mAppEntry;
@Mock
private Fragment mFragment;
private Context mShadowContext;
private LayoutInflater mLayoutInflater;
private PackageInfo mInfo;
private AppHeaderController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowContext = ShadowApplication.getInstance().getApplicationContext();
mLayoutInflater = LayoutInflater.from(mShadowContext);
mInfo = new PackageInfo();
mInfo.versionName = "1234";
mController = new AppHeaderController(mContext);
}
@Test
public void bindViews_shouldBindAllData() {
final String testString = "test";
final View appHeader = mLayoutInflater.inflate(R.layout.app_details, null /* root */);
final TextView label = (TextView) appHeader.findViewById(android.R.id.title);
final TextView version = (TextView) appHeader.findViewById(android.R.id.summary);
label.setText(testString);
label.setText(testString);
mController.bindAppHeader(appHeader, mInfo, mAppEntry);
assertThat(label.getText()).isNotEqualTo(testString);
assertThat(version.getText())
.isEqualTo(mShadowContext.getString(R.string.version_text, mInfo.versionName));
}
@Test
public void bindButton_hasAppPref_shouldShowButton() {
final ResolveInfo info = new ResolveInfo();
info.activityInfo = new ActivityInfo();
info.activityInfo.packageName = "123";
info.activityInfo.name = "321";
final View appLinks = mLayoutInflater
.inflate(R.layout.app_details, null /* root */);
when(mContext.getPackageManager().resolveActivity(any(Intent.class), anyInt()))
.thenReturn(info);
mController.bindAppHeaderButtons(mFragment, appLinks,
mShadowContext.getPackageName(),
AppHeaderController.ActionType.ACTION_APP_PREFERENCE,
AppHeaderController.ActionType.ACTION_APP_PREFERENCE);
assertThat(appLinks.findViewById(R.id.left_button).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(appLinks.findViewById(R.id.right_button).getVisibility())
.isEqualTo(View.VISIBLE);
try {
appLinks.findViewById(R.id.left_button).performClick();
} catch (Exception e) {
// Ignore exception because the launching intent is fake.
}
verify(mFragment).startActivity(any(Intent.class));
}
@Test
public void bindButton_noAppPref_shouldNotShowButton() {
final View appLinks = mLayoutInflater
.inflate(R.layout.app_details, null /* root */);
when(mContext.getPackageManager().resolveActivity(any(Intent.class), anyInt()))
.thenReturn(null);
mController.bindAppHeaderButtons(mFragment, appLinks,
mShadowContext.getPackageName(),
AppHeaderController.ActionType.ACTION_APP_PREFERENCE,
AppHeaderController.ActionType.ACTION_APP_PREFERENCE);
assertThat(appLinks.findViewById(R.id.left_button).getVisibility())
.isEqualTo(View.GONE);
assertThat(appLinks.findViewById(R.id.right_button).getVisibility())
.isEqualTo(View.GONE);
}
@Test
public void bindButton_noStoreLink_shouldNotShowButton() {
final View appLinks = mLayoutInflater
.inflate(R.layout.app_details, null /* root */);
when(mContext.getPackageManager().resolveActivity(any(Intent.class), anyInt()))
.thenReturn(null);
mController.bindAppHeaderButtons(mFragment, appLinks,
mShadowContext.getPackageName(),
AppHeaderController.ActionType.ACTION_STORE_DEEP_LINK,
AppHeaderController.ActionType.ACTION_STORE_DEEP_LINK);
assertThat(appLinks.findViewById(R.id.left_button).getVisibility())
.isEqualTo(View.GONE);
assertThat(appLinks.findViewById(R.id.right_button).getVisibility())
.isEqualTo(View.GONE);
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.testutils;
import android.content.Context;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -38,6 +40,7 @@ public class FakeFeatureFactory extends FeatureFactory {
public final PowerUsageFeatureProvider powerUsageFeatureProvider;
public final DashboardFeatureProvider dashboardFeatureProvider;
public final LocaleFeatureProvider localeFeatureProvider;
public final ApplicationFeatureProvider applicationFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -65,6 +68,7 @@ public class FakeFeatureFactory extends FeatureFactory {
powerUsageFeatureProvider = mock(PowerUsageFeatureProvider.class);
dashboardFeatureProvider = mock(DashboardFeatureProvider.class);
localeFeatureProvider = mock(LocaleFeatureProvider.class);
applicationFeatureProvider = mock(ApplicationFeatureProvider.class);
}
@Override
@@ -87,6 +91,11 @@ public class FakeFeatureFactory extends FeatureFactory {
return dashboardFeatureProvider;
}
@Override
public ApplicationFeatureProvider getApplicationFeatureProvider(Context context) {
return applicationFeatureProvider;
}
@Override
public LocaleFeatureProvider getLocaleFeatureProvider() {
return localeFeatureProvider;