Remove DashboardSummary and all related classes.
- Condition is already supported in PersonalSettingsFragment - Suggestion is already supported in PersonalSettingsFragment - Static/dynamic tiles are supported in TopLevelSettings Change-Id: I51882e3bd0919ad95109baefac683d98667c11e3 Fixes: 110405144 Test: robotests
This commit is contained in:
@@ -191,12 +191,6 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.VOICE_LAUNCH" />
|
<category android:name="android.intent.category.VOICE_LAUNCH" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS"/>
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="11"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage"/>
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.network.NetworkDashboardFragment"/>
|
android:value="com.android.settings.network.NetworkDashboardFragment"/>
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
@@ -213,14 +207,8 @@
|
|||||||
<action android:name="android.settings.BLUETOOTH_SETTINGS" />
|
<action android:name="android.settings.BLUETOOTH_SETTINGS" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS"/>
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="10"/>
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"/>
|
android:value="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"/>
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage"/>
|
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
</activity>
|
</activity>
|
||||||
@@ -897,18 +885,10 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="com.android.settings.SHORTCUT" />
|
<category android:name="com.android.settings.SHORTCUT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="7"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage" />
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.DisplaySettings" />
|
android:value="com.android.settings.DisplaySettings" />
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
<meta-data android:name="com.android.settings.summary"
|
|
||||||
android:resource="@string/display_dashboard_summary"/>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
@@ -1262,12 +1242,6 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.VOICE_LAUNCH" />
|
<category android:name="android.intent.category.VOICE_LAUNCH" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="4"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage" />
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.security.SecuritySettings" />
|
android:value="com.android.settings.security.SecuritySettings" />
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
@@ -1431,18 +1405,10 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="com.android.settings.SHORTCUT" />
|
<category android:name="com.android.settings.SHORTCUT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="2"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage" />
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.accessibility.AccessibilitySettings" />
|
android:value="com.android.settings.accessibility.AccessibilitySettings" />
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
<meta-data android:name="com.android.settings.summary"
|
|
||||||
android:resource="@string/accessibility_settings_summary" />
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".accessibility.AccessibilitySettingsForSetupWizardActivity"
|
<activity android:name=".accessibility.AccessibilitySettingsForSetupWizardActivity"
|
||||||
@@ -1772,14 +1738,6 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="com.android.settings.SHORTCUT" />
|
<category android:name="com.android.settings.SHORTCUT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="5"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage" />
|
|
||||||
<meta-data android:name="com.android.settings.title"
|
|
||||||
android:resource="@string/storage_usb_settings" />
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.deviceinfo.StorageSettings" />
|
android:value="com.android.settings.deviceinfo.StorageSettings" />
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
@@ -2171,12 +2129,6 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="com.android.settings.SHORTCUT" />
|
<category android:name="com.android.settings.SHORTCUT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="8"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage" />
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.fuelgauge.PowerUsageSummary" />
|
android:value="com.android.settings.fuelgauge.PowerUsageSummary" />
|
||||||
</activity>
|
</activity>
|
||||||
@@ -2545,18 +2497,10 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="com.android.settings.SHORTCUT" />
|
<category android:name="com.android.settings.SHORTCUT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.order" android:value="6"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage" />
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.notification.SoundSettings" />
|
android:value="com.android.settings.notification.SoundSettings" />
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
<meta-data android:name="com.android.settings.summary"
|
|
||||||
android:resource="@string/sound_dashboard_summary"/>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- Show apps for which application-level notification settings are applicable -->
|
<!-- Show apps for which application-level notification settings are applicable -->
|
||||||
@@ -2954,13 +2898,8 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.android.settings.action.SETTINGS"/>
|
<action android:name="com.android.settings.action.SETTINGS"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data android:name="com.android.settings.order" android:value="9"/>
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.applications.AppAndNotificationDashboardFragment"/>
|
android:value="com.android.settings.applications.AppAndNotificationDashboardFragment"/>
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage"/>
|
|
||||||
<meta-data android:name="com.android.settings.summary"
|
|
||||||
android:resource="@string/app_and_notification_dashboard_summary"/>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
@@ -2969,9 +2908,6 @@
|
|||||||
android:icon="@drawable/ic_homepage_accounts"
|
android:icon="@drawable/ic_homepage_accounts"
|
||||||
android:taskAffinity="com.android.settings"
|
android:taskAffinity="com.android.settings"
|
||||||
android:parentActivityName="Settings">
|
android:parentActivityName="Settings">
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter android:priority="1">
|
<intent-filter android:priority="1">
|
||||||
<action android:name="android.settings.SYNC_SETTINGS" />
|
<action android:name="android.settings.SYNC_SETTINGS" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
@@ -2980,11 +2916,8 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="com.android.settings.SHORTCUT" />
|
<category android:name="com.android.settings.SHORTCUT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data android:name="com.android.settings.order" android:value="3"/>
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.accounts.AccountDashboardFragment"/>
|
android:value="com.android.settings.accounts.AccountDashboardFragment"/>
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage"/>
|
|
||||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
</activity>
|
</activity>
|
||||||
@@ -2995,20 +2928,12 @@
|
|||||||
android:icon="@drawable/ic_homepage_system_dashboard"
|
android:icon="@drawable/ic_homepage_system_dashboard"
|
||||||
android:taskAffinity="com.android.settings"
|
android:taskAffinity="com.android.settings"
|
||||||
android:parentActivityName="Settings">
|
android:parentActivityName="Settings">
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter android:priority="70">
|
<intent-filter android:priority="70">
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="com.android.settings.SHORTCUT" />
|
<category android:name="com.android.settings.SHORTCUT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data android:name="com.android.settings.order" android:value="-1"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage"/>
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.system.SystemDashboardFragment"/>
|
android:value="com.android.settings.system.SystemDashboardFragment"/>
|
||||||
<meta-data android:name="com.android.settings.summary"
|
|
||||||
android:resource="@string/system_dashboard_summary"/>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".support.SupportDashboardActivity"
|
<activity android:name=".support.SupportDashboardActivity"
|
||||||
@@ -3016,18 +2941,10 @@
|
|||||||
android:icon="@drawable/ic_homepage_support"
|
android:icon="@drawable/ic_homepage_support"
|
||||||
android:theme="@android:style/Theme.DeviceDefault.Light.Panel"
|
android:theme="@android:style/Theme.DeviceDefault.Light.Panel"
|
||||||
android:enabled="@bool/config_support_enabled">
|
android:enabled="@bool/config_support_enabled">
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.android.settings.action.SETTINGS"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.android.settings.action.SUPPORT_SETTINGS" />
|
<action android:name="com.android.settings.action.SUPPORT_SETTINGS" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data android:name="com.android.settings.order" android:value="-2"/>
|
|
||||||
<meta-data android:name="com.android.settings.category"
|
|
||||||
android:value="com.android.settings.category.ia.homepage"/>
|
|
||||||
<meta-data android:name="com.android.settings.summary"
|
|
||||||
android:resource="@string/support_summary"/>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
@@ -3076,6 +2993,7 @@
|
|||||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".development.qstile.DevelopmentTiles$WinscopeTrace"
|
android:name=".development.qstile.DevelopmentTiles$WinscopeTrace"
|
||||||
android:label="@string/winscope_trace_quick_settings_title"
|
android:label="@string/winscope_trace_quick_settings_title"
|
||||||
|
@@ -61,8 +61,6 @@ import com.android.settings.core.SettingsBaseActivity;
|
|||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
import com.android.settings.core.gateway.SettingsGateway;
|
import com.android.settings.core.gateway.SettingsGateway;
|
||||||
import com.android.settings.dashboard.DashboardFeatureProvider;
|
import com.android.settings.dashboard.DashboardFeatureProvider;
|
||||||
import com.android.settings.dashboard.DashboardSummary;
|
|
||||||
import com.android.settings.homepage.SettingsHomepageActivity;
|
|
||||||
import com.android.settings.homepage.TopLevelSettings;
|
import com.android.settings.homepage.TopLevelSettings;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.wfd.WifiDisplaySettings;
|
import com.android.settings.wfd.WifiDisplaySettings;
|
||||||
@@ -393,14 +391,9 @@ public class SettingsActivity extends SettingsBaseActivity
|
|||||||
} else {
|
} else {
|
||||||
// Show search icon as up affordance if we are displaying the main Dashboard
|
// Show search icon as up affordance if we are displaying the main Dashboard
|
||||||
mInitialTitleResId = R.string.dashboard_title;
|
mInitialTitleResId = R.string.dashboard_title;
|
||||||
|
switchToFragment(TopLevelSettings.class.getName(), null /* args */, false, false,
|
||||||
|
mInitialTitleResId, mInitialTitle, false);
|
||||||
|
|
||||||
if (SettingsHomepageActivity.isDynamicHomepageEnabled(this)) {
|
|
||||||
switchToFragment(TopLevelSettings.class.getName(), null /* args */, false, false,
|
|
||||||
mInitialTitleResId, mInitialTitle, false);
|
|
||||||
} else {
|
|
||||||
switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
|
|
||||||
mInitialTitleResId, mInitialTitle, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,22 +18,16 @@ package com.android.settings.accounts;
|
|||||||
import static android.provider.Settings.EXTRA_AUTHORITIES;
|
import static android.provider.Settings.EXTRA_AUTHORITIES;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.icu.text.ListFormatter;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
import android.text.BidiFormatter;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.dashboard.SummaryLoader;
|
|
||||||
import com.android.settings.search.BaseSearchIndexProvider;
|
import com.android.settings.search.BaseSearchIndexProvider;
|
||||||
import com.android.settings.users.AutoSyncDataPreferenceController;
|
import com.android.settings.users.AutoSyncDataPreferenceController;
|
||||||
import com.android.settings.users.AutoSyncPersonalDataPreferenceController;
|
import com.android.settings.users.AutoSyncPersonalDataPreferenceController;
|
||||||
import com.android.settings.users.AutoSyncWorkDataPreferenceController;
|
import com.android.settings.users.AutoSyncWorkDataPreferenceController;
|
||||||
import com.android.settingslib.accounts.AuthenticatorHelper;
|
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
import com.android.settingslib.search.SearchIndexable;
|
import com.android.settingslib.search.SearchIndexable;
|
||||||
|
|
||||||
@@ -89,49 +83,6 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
return controllers;
|
return controllers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final SummaryLoader mSummaryLoader;
|
|
||||||
|
|
||||||
public SummaryProvider(Context context, SummaryLoader summaryLoader) {
|
|
||||||
mContext = context;
|
|
||||||
mSummaryLoader = summaryLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setListening(boolean listening) {
|
|
||||||
if (listening) {
|
|
||||||
final AuthenticatorHelper authHelper = new AuthenticatorHelper(mContext,
|
|
||||||
UserHandle.of(UserHandle.myUserId()), null /* OnAccountsUpdateListener */);
|
|
||||||
final String[] types = authHelper.getEnabledAccountTypes();
|
|
||||||
final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
|
|
||||||
final List<CharSequence> summaries = new ArrayList<>();
|
|
||||||
|
|
||||||
if (types == null || types.length == 0) {
|
|
||||||
summaries.add(mContext.getString(R.string.account_dashboard_default_summary));
|
|
||||||
} else {
|
|
||||||
// Show up to 3 account types, ignore any null value
|
|
||||||
int accountToAdd = Math.min(3, types.length);
|
|
||||||
|
|
||||||
for (int i = 0; i < types.length && accountToAdd > 0; i++) {
|
|
||||||
final CharSequence label = authHelper.getLabelForType(mContext, types[i]);
|
|
||||||
if (TextUtils.isEmpty(label)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
summaries.add(bidiFormatter.unicodeWrap(label));
|
|
||||||
accountToAdd--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mSummaryLoader.setSummary(this, ListFormatter.getInstance().format(summaries));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
|
|
||||||
= (activity, summaryLoader) -> new SummaryProvider(activity, summaryLoader);
|
|
||||||
|
|
||||||
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
new BaseSearchIndexProvider() {
|
new BaseSearchIndexProvider() {
|
||||||
@Override
|
@Override
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.settings.connecteddevice;
|
package com.android.settings.connecteddevice;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
|
|
||||||
@@ -24,7 +23,6 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import com.android.internal.logging.nano.MetricsProto;
|
import com.android.internal.logging.nano.MetricsProto;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.dashboard.SummaryLoader;
|
|
||||||
import com.android.settings.search.BaseSearchIndexProvider;
|
import com.android.settings.search.BaseSearchIndexProvider;
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
@@ -92,35 +90,6 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
|
|||||||
use(DiscoverableFooterPreferenceController.class).init(this);
|
use(DiscoverableFooterPreferenceController.class).init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static class SummaryProvider implements SummaryLoader.SummaryProvider {
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final SummaryLoader mSummaryLoader;
|
|
||||||
|
|
||||||
public SummaryProvider(Context context, SummaryLoader summaryLoader) {
|
|
||||||
mContext = context;
|
|
||||||
mSummaryLoader = summaryLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setListening(boolean listening) {
|
|
||||||
if (listening) {
|
|
||||||
mSummaryLoader.setSummary(this, mContext.getText(AdvancedConnectedDeviceController.
|
|
||||||
getConnectedDevicesSummaryResourceId(mContext)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
|
|
||||||
= new SummaryLoader.SummaryProviderFactory() {
|
|
||||||
@Override
|
|
||||||
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
|
|
||||||
SummaryLoader summaryLoader) {
|
|
||||||
return new SummaryProvider(activity, summaryLoader);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For Search.
|
* For Search.
|
||||||
*/
|
*/
|
||||||
|
@@ -1,407 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 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.dashboard;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.R.id;
|
|
||||||
import com.android.settings.dashboard.DashboardData.ConditionHeaderData;
|
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionAdapter;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionManager;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionalCard;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settings.widget.RoundedHomepageIcon;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
|
|
||||||
import com.android.settingslib.utils.IconCache;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
|
|
||||||
implements SummaryLoader.SummaryConsumer, SuggestionAdapter.Callback, LifecycleObserver,
|
|
||||||
OnSaveInstanceState {
|
|
||||||
public static final String TAG = "DashboardAdapter";
|
|
||||||
private static final String STATE_CATEGORY_LIST = "category_list";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String STATE_CONDITION_EXPANDED = "condition_expanded";
|
|
||||||
|
|
||||||
private final IconCache mCache;
|
|
||||||
private final Context mContext;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private final DashboardFeatureProvider mDashboardFeatureProvider;
|
|
||||||
private boolean mFirstFrameDrawn;
|
|
||||||
private RecyclerView mRecyclerView;
|
|
||||||
private SuggestionAdapter mSuggestionAdapter;
|
|
||||||
private ConditionManager mConditionManager;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
DashboardData mDashboardData;
|
|
||||||
|
|
||||||
private View.OnClickListener mTileClickListener = new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
//TODO: get rid of setTag/getTag
|
|
||||||
mDashboardFeatureProvider.openTileIntent((FragmentActivity) mContext,
|
|
||||||
(Tile) v.getTag());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public DashboardAdapter(Context context, Bundle savedInstanceState,
|
|
||||||
ConditionManager conditionManager,
|
|
||||||
SuggestionControllerMixinCompat suggestionControllerMixin, Lifecycle lifecycle) {
|
|
||||||
|
|
||||||
DashboardCategory category = null;
|
|
||||||
boolean conditionExpanded = false;
|
|
||||||
|
|
||||||
mContext = context;
|
|
||||||
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
|
||||||
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
|
||||||
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
|
|
||||||
mCache = new IconCache(context);
|
|
||||||
mConditionManager = conditionManager;
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, suggestionControllerMixin,
|
|
||||||
savedInstanceState, this /* callback */, lifecycle);
|
|
||||||
|
|
||||||
setHasStableIds(true);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
|
|
||||||
conditionExpanded = savedInstanceState.getBoolean(
|
|
||||||
STATE_CONDITION_EXPANDED, conditionExpanded);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lifecycle != null) {
|
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
mDashboardData = new DashboardData.Builder()
|
|
||||||
.setSuggestions(mSuggestionAdapter.getSuggestions())
|
|
||||||
.setCategory(category)
|
|
||||||
.setConditionExpanded(conditionExpanded)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSuggestions(List<Suggestion> data) {
|
|
||||||
final DashboardData prevData = mDashboardData;
|
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
|
||||||
.setSuggestions(data)
|
|
||||||
.build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCategory(DashboardCategory category) {
|
|
||||||
final DashboardData prevData = mDashboardData;
|
|
||||||
Log.d(TAG, "adapter setCategory called");
|
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
|
||||||
.setCategory(category)
|
|
||||||
.build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConditions(List<ConditionalCard> conditions) {
|
|
||||||
final DashboardData prevData = mDashboardData;
|
|
||||||
Log.d(TAG, "adapter setConditions called");
|
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
|
||||||
.setConditions(conditions)
|
|
||||||
.build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuggestionClosed(Suggestion suggestion) {
|
|
||||||
final List<Suggestion> list = mDashboardData.getSuggestions();
|
|
||||||
if (list == null || list.size() == 0 || !list.remove(suggestion)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
// The only suggestion is dismissed, and the the empty suggestion container will
|
|
||||||
// remain as the dashboard item. Need to refresh the dashboard list.
|
|
||||||
setSuggestions(null);
|
|
||||||
} else {
|
|
||||||
setSuggestions(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifySummaryChanged(Tile tile) {
|
|
||||||
final int position = mDashboardData.getPositionByTile(tile);
|
|
||||||
if (position != DashboardData.POSITION_NOT_FOUND) {
|
|
||||||
// Since usually tile in parameter and tile in mCategories are same instance,
|
|
||||||
// which is hard to be detected by DiffUtil, so we notifyItemChanged directly.
|
|
||||||
notifyItemChanged(position, mDashboardData.getItemTypeByPosition(position));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
|
|
||||||
if (viewType == R.layout.condition_header) {
|
|
||||||
return new ConditionHeaderHolder(view);
|
|
||||||
}
|
|
||||||
if (viewType == R.layout.condition_container) {
|
|
||||||
return new ConditionContainerHolder(view);
|
|
||||||
}
|
|
||||||
if (viewType == R.layout.suggestion_container) {
|
|
||||||
return new SuggestionContainerHolder(view);
|
|
||||||
}
|
|
||||||
return new DashboardItemHolder(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
|
||||||
final int type = mDashboardData.getItemTypeByPosition(position);
|
|
||||||
switch (type) {
|
|
||||||
case R.layout.dashboard_tile:
|
|
||||||
final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
|
|
||||||
onBindTile(holder, tile);
|
|
||||||
holder.itemView.setTag(tile);
|
|
||||||
holder.itemView.setOnClickListener(mTileClickListener);
|
|
||||||
break;
|
|
||||||
case R.layout.suggestion_container:
|
|
||||||
onBindSuggestion((SuggestionContainerHolder) holder, position);
|
|
||||||
break;
|
|
||||||
case R.layout.condition_container:
|
|
||||||
onBindCondition((ConditionContainerHolder) holder, position);
|
|
||||||
break;
|
|
||||||
case R.layout.condition_header:
|
|
||||||
onBindConditionHeader((ConditionHeaderHolder) holder,
|
|
||||||
(ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
|
|
||||||
break;
|
|
||||||
case R.layout.condition_footer:
|
|
||||||
holder.itemView.setOnClickListener(v -> {
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
|
|
||||||
DashboardData prevData = mDashboardData;
|
|
||||||
mDashboardData = new DashboardData.Builder(prevData).
|
|
||||||
setConditionExpanded(false).build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
scrollToTopOfConditions();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return mDashboardData.getItemIdByPosition(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
return mDashboardData.getItemTypeByPosition(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mDashboardData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
|
|
||||||
super.onAttachedToRecyclerView(recyclerView);
|
|
||||||
// save the view so that we can scroll it when expanding/collapsing the suggestion and
|
|
||||||
// conditions.
|
|
||||||
mRecyclerView = recyclerView;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItem(long itemId) {
|
|
||||||
return mDashboardData.getItemEntityById(itemId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void notifyDashboardDataChanged(DashboardData prevData) {
|
|
||||||
if (mFirstFrameDrawn && prevData != null) {
|
|
||||||
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
|
|
||||||
.ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
|
|
||||||
diffResult.dispatchUpdatesTo(this);
|
|
||||||
} else {
|
|
||||||
mFirstFrameDrawn = true;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindConditionHeader(final ConditionHeaderHolder holder, ConditionHeaderData data) {
|
|
||||||
holder.icon.setImageDrawable(data.conditionIcons.get(0));
|
|
||||||
if (data.conditionCount == 1) {
|
|
||||||
holder.title.setText(data.title);
|
|
||||||
holder.summary.setText(null);
|
|
||||||
holder.icons.setVisibility(View.INVISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.title.setText(null);
|
|
||||||
holder.summary.setText(
|
|
||||||
mContext.getString(R.string.condition_summary, data.conditionCount));
|
|
||||||
updateConditionIcons(data.conditionIcons, holder.icons);
|
|
||||||
holder.icons.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.itemView.setOnClickListener(v -> {
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
|
|
||||||
final DashboardData prevData = mDashboardData;
|
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
|
||||||
.setConditionExpanded(true).build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
scrollToTopOfConditions();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindCondition(final ConditionContainerHolder holder, int position) {
|
|
||||||
final List<ConditionalCard> conditions =
|
|
||||||
(List) mDashboardData.getItemEntityByPosition(position);
|
|
||||||
final ConditionAdapter adapter = new ConditionAdapter(
|
|
||||||
mContext, mConditionManager, conditions,
|
|
||||||
mDashboardData.isConditionExpanded());
|
|
||||||
holder.data.setAdapter(adapter);
|
|
||||||
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindSuggestion(final SuggestionContainerHolder holder, int position) {
|
|
||||||
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
|
|
||||||
// header anymore.
|
|
||||||
final List<Suggestion> suggestions =
|
|
||||||
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
|
|
||||||
if (suggestions != null && suggestions.size() > 0) {
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
holder.data.setAdapter(mSuggestionAdapter);
|
|
||||||
}
|
|
||||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
|
|
||||||
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
|
||||||
holder.data.setLayoutManager(layoutManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindTile(DashboardItemHolder holder, Tile tile) {
|
|
||||||
Icon tileIcon = tile.getIcon(mContext);
|
|
||||||
Drawable icon = mCache.getIcon(tileIcon);
|
|
||||||
if (!TextUtils.equals(tileIcon.getResPackage(), mContext.getPackageName())
|
|
||||||
&& !(icon instanceof RoundedHomepageIcon)) {
|
|
||||||
icon = new RoundedHomepageIcon(mContext, icon);
|
|
||||||
((RoundedHomepageIcon) icon).setBackgroundColor(mContext, tile);
|
|
||||||
mCache.updateIcon(tileIcon, icon);
|
|
||||||
}
|
|
||||||
holder.icon.setImageDrawable(icon);
|
|
||||||
holder.title.setText(tile.getTitle(mContext));
|
|
||||||
final CharSequence summary = tile.getSummary(mContext);
|
|
||||||
if (!TextUtils.isEmpty(summary)) {
|
|
||||||
holder.summary.setText(summary);
|
|
||||||
holder.summary.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.summary.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
final DashboardCategory category = mDashboardData.getCategory();
|
|
||||||
if (category != null) {
|
|
||||||
outState.putParcelable(STATE_CATEGORY_LIST, category);
|
|
||||||
}
|
|
||||||
outState.putBoolean(STATE_CONDITION_EXPANDED, mDashboardData.isConditionExpanded());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateConditionIcons(List<Drawable> icons, ViewGroup parent) {
|
|
||||||
if (icons == null || icons.size() < 2) {
|
|
||||||
parent.setVisibility(View.INVISIBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
|
||||||
parent.removeAllViews();
|
|
||||||
for (int i = 1, size = icons.size(); i < size; i++) {
|
|
||||||
ImageView icon = (ImageView) inflater.inflate(
|
|
||||||
R.layout.condition_header_icon, parent, false);
|
|
||||||
icon.setImageDrawable(icons.get(i));
|
|
||||||
parent.addView(icon);
|
|
||||||
}
|
|
||||||
parent.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scrollToTopOfConditions() {
|
|
||||||
mRecyclerView.scrollToPosition(mDashboardData.hasSuggestion() ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
|
|
||||||
public final ImageView icon;
|
|
||||||
public final TextView title;
|
|
||||||
public final TextView summary;
|
|
||||||
|
|
||||||
public DashboardItemHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
icon = itemView.findViewById(android.R.id.icon);
|
|
||||||
title = itemView.findViewById(android.R.id.title);
|
|
||||||
summary = itemView.findViewById(android.R.id.summary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConditionHeaderHolder extends DashboardItemHolder {
|
|
||||||
public final LinearLayout icons;
|
|
||||||
public final ImageView expandIndicator;
|
|
||||||
|
|
||||||
public ConditionHeaderHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
icons = itemView.findViewById(id.additional_icons);
|
|
||||||
expandIndicator = itemView.findViewById(id.expand_indicator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConditionContainerHolder extends DashboardItemHolder {
|
|
||||||
public final RecyclerView data;
|
|
||||||
|
|
||||||
public ConditionContainerHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
data = itemView.findViewById(id.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SuggestionContainerHolder extends DashboardItemHolder {
|
|
||||||
public final RecyclerView data;
|
|
||||||
|
|
||||||
public SuggestionContainerHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
data = itemView.findViewById(id.suggestion_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,416 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.dashboard;
|
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionalCard;
|
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Description about data list used in the DashboardAdapter. In the data list each item can be
|
|
||||||
* Condition, suggestion or category tile.
|
|
||||||
* <p>
|
|
||||||
* ItemsData has inner class Item, which represents the Item in data list.
|
|
||||||
*/
|
|
||||||
public class DashboardData {
|
|
||||||
public static final int POSITION_NOT_FOUND = -1;
|
|
||||||
|
|
||||||
// stable id for different type of items.
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_SUGGESTION_CONTAINER = 0;
|
|
||||||
static final int STABLE_ID_SUGGESTION_CONDITION_DIVIDER = 1;
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_CONDITION_HEADER = 2;
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_CONDITION_FOOTER = 3;
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_CONDITION_CONTAINER = 4;
|
|
||||||
|
|
||||||
private final List<Item> mItems;
|
|
||||||
private final DashboardCategory mCategory;
|
|
||||||
private final List<ConditionalCard> mConditions;
|
|
||||||
private final List<Suggestion> mSuggestions;
|
|
||||||
private final boolean mConditionExpanded;
|
|
||||||
|
|
||||||
private DashboardData(Builder builder) {
|
|
||||||
mCategory = builder.mCategory;
|
|
||||||
mConditions = builder.mConditions;
|
|
||||||
mSuggestions = builder.mSuggestions;
|
|
||||||
mConditionExpanded = builder.mConditionExpanded;
|
|
||||||
mItems = new ArrayList<>();
|
|
||||||
|
|
||||||
buildItemsData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getItemIdByPosition(int position) {
|
|
||||||
return mItems.get(position).id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getItemTypeByPosition(int position) {
|
|
||||||
return mItems.get(position).type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItemEntityByPosition(int position) {
|
|
||||||
return mItems.get(position).entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Item> getItemList() {
|
|
||||||
return mItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return mItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItemEntityById(long id) {
|
|
||||||
for (final Item item : mItems) {
|
|
||||||
if (item.id == id) {
|
|
||||||
return item.entity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DashboardCategory getCategory() {
|
|
||||||
return mCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ConditionalCard> getConditions() {
|
|
||||||
return mConditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Suggestion> getSuggestions() {
|
|
||||||
return mSuggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSuggestion() {
|
|
||||||
return sizeOf(mSuggestions) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConditionExpanded() {
|
|
||||||
return mConditionExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the position of the object in mItems list, using the equals method to compare
|
|
||||||
*
|
|
||||||
* @param entity the object that need to be found in list
|
|
||||||
* @return position of the object, return POSITION_NOT_FOUND if object isn't in the list
|
|
||||||
*/
|
|
||||||
public int getPositionByEntity(Object entity) {
|
|
||||||
if (entity == null) return POSITION_NOT_FOUND;
|
|
||||||
|
|
||||||
final int size = mItems.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
final Object item = mItems.get(i).entity;
|
|
||||||
if (entity.equals(item)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return POSITION_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the position of the Tile object.
|
|
||||||
* <p>
|
|
||||||
* First, try to find the exact identical instance of the tile object, if not found,
|
|
||||||
* then try to find a tile has the same id.
|
|
||||||
*
|
|
||||||
* @param tile tile that need to be found
|
|
||||||
* @return position of the object, return INDEX_NOT_FOUND if object isn't in the list
|
|
||||||
*/
|
|
||||||
public int getPositionByTile(Tile tile) {
|
|
||||||
final int size = mItems.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
final Object entity = mItems.get(i).entity;
|
|
||||||
if (entity == tile) {
|
|
||||||
return i;
|
|
||||||
} else if (entity instanceof Tile && tile.getId() == ((Tile) entity).getId()) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return POSITION_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add item into list when {@paramref add} is true.
|
|
||||||
*
|
|
||||||
* @param item maybe {@link ConditionalCard}, {@link Tile}, {@link DashboardCategory}
|
|
||||||
* or null
|
|
||||||
* @param type type of the item, and value is the layout id
|
|
||||||
* @param stableId The stable id for this item
|
|
||||||
* @param add flag about whether to add item into list
|
|
||||||
*/
|
|
||||||
private void addToItemList(Object item, int type, int stableId, boolean add) {
|
|
||||||
if (add) {
|
|
||||||
mItems.add(new Item(item, type, stableId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the mItems list using mConditions, mSuggestions, mCategories data
|
|
||||||
* and mIsShowingAll, mConditionExpanded flag.
|
|
||||||
*/
|
|
||||||
private void buildItemsData() {
|
|
||||||
final List<ConditionalCard> conditions = mConditions;
|
|
||||||
final boolean hasConditions = sizeOf(conditions) > 0;
|
|
||||||
|
|
||||||
final List<Suggestion> suggestions = mSuggestions;
|
|
||||||
final boolean hasSuggestions = sizeOf(suggestions) > 0;
|
|
||||||
|
|
||||||
/* Suggestion container. This is the card view that contains the list of suggestions.
|
|
||||||
* This will be added whenever the suggestion list is not empty */
|
|
||||||
addToItemList(suggestions, R.layout.suggestion_container,
|
|
||||||
STABLE_ID_SUGGESTION_CONTAINER, hasSuggestions);
|
|
||||||
|
|
||||||
/* Divider between suggestion and conditions if both are present. */
|
|
||||||
addToItemList(null /* item */, R.layout.horizontal_divider,
|
|
||||||
STABLE_ID_SUGGESTION_CONDITION_DIVIDER, hasSuggestions && hasConditions);
|
|
||||||
|
|
||||||
/* Condition header. This will be present when there is condition and it is collapsed */
|
|
||||||
addToItemList(new ConditionHeaderData(conditions),
|
|
||||||
R.layout.condition_header,
|
|
||||||
STABLE_ID_CONDITION_HEADER, hasConditions && !mConditionExpanded);
|
|
||||||
|
|
||||||
/* Condition container. This is the card view that contains the list of conditions.
|
|
||||||
* This will be added whenever the condition list is not empty and expanded */
|
|
||||||
addToItemList(conditions, R.layout.condition_container,
|
|
||||||
STABLE_ID_CONDITION_CONTAINER, hasConditions && mConditionExpanded);
|
|
||||||
|
|
||||||
/* Condition footer. This will be present when there is condition and it is expanded */
|
|
||||||
addToItemList(null /* item */, R.layout.condition_footer,
|
|
||||||
STABLE_ID_CONDITION_FOOTER, hasConditions && mConditionExpanded);
|
|
||||||
|
|
||||||
if (mCategory != null) {
|
|
||||||
final List<Tile> tiles = mCategory.getTiles();
|
|
||||||
for (int i = 0; i < tiles.size(); i++) {
|
|
||||||
final Tile tile = tiles.get(i);
|
|
||||||
addToItemList(tile, R.layout.dashboard_tile, tile.getId(),
|
|
||||||
true /* add */);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int sizeOf(List<?> list) {
|
|
||||||
return list == null ? 0 : list.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builder used to build the ItemsData
|
|
||||||
*/
|
|
||||||
public static class Builder {
|
|
||||||
private DashboardCategory mCategory;
|
|
||||||
private List<ConditionalCard> mConditions;
|
|
||||||
private List<Suggestion> mSuggestions;
|
|
||||||
private boolean mConditionExpanded;
|
|
||||||
|
|
||||||
public Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder(DashboardData dashboardData) {
|
|
||||||
mCategory = dashboardData.mCategory;
|
|
||||||
mConditions = dashboardData.mConditions;
|
|
||||||
mSuggestions = dashboardData.mSuggestions;
|
|
||||||
mConditionExpanded = dashboardData.mConditionExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setCategory(DashboardCategory category) {
|
|
||||||
this.mCategory = category;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConditions(List<ConditionalCard> conditions) {
|
|
||||||
this.mConditions = conditions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setSuggestions(List<Suggestion> suggestions) {
|
|
||||||
this.mSuggestions = suggestions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConditionExpanded(boolean expanded) {
|
|
||||||
this.mConditionExpanded = expanded;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DashboardData build() {
|
|
||||||
return new DashboardData(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A DiffCallback to calculate the difference between old and new Item
|
|
||||||
* List in DashboardData
|
|
||||||
*/
|
|
||||||
public static class ItemsDataDiffCallback extends DiffUtil.Callback {
|
|
||||||
final private List<Item> mOldItems;
|
|
||||||
final private List<Item> mNewItems;
|
|
||||||
|
|
||||||
public ItemsDataDiffCallback(List<Item> oldItems, List<Item> newItems) {
|
|
||||||
mOldItems = oldItems;
|
|
||||||
mNewItems = newItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOldListSize() {
|
|
||||||
return mOldItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getNewListSize() {
|
|
||||||
return mNewItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
|
||||||
return mOldItems.get(oldItemPosition).id == mNewItems.get(newItemPosition).id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
|
||||||
return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An item contains the data needed in the DashboardData.
|
|
||||||
*/
|
|
||||||
static class Item {
|
|
||||||
// valid types in field type
|
|
||||||
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
|
|
||||||
private static final int TYPE_SUGGESTION_CONTAINER =
|
|
||||||
R.layout.suggestion_container;
|
|
||||||
private static final int TYPE_CONDITION_CONTAINER =
|
|
||||||
R.layout.condition_container;
|
|
||||||
private static final int TYPE_CONDITION_HEADER =
|
|
||||||
R.layout.condition_header;
|
|
||||||
private static final int TYPE_CONDITION_FOOTER =
|
|
||||||
R.layout.condition_footer;
|
|
||||||
private static final int TYPE_SUGGESTION_CONDITION_DIVIDER = R.layout.horizontal_divider;
|
|
||||||
|
|
||||||
@IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONTAINER, TYPE_CONDITION_CONTAINER,
|
|
||||||
TYPE_CONDITION_HEADER, TYPE_CONDITION_FOOTER, TYPE_SUGGESTION_CONDITION_DIVIDER})
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
public @interface ItemTypes {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main data object in item, usually is a {@link Tile}, {@link ConditionalCard}
|
|
||||||
* object. This object can also be null when the
|
|
||||||
* item is an divider line. Please refer to {@link #buildItemsData()} for
|
|
||||||
* detail usage of the Item.
|
|
||||||
*/
|
|
||||||
public final Object entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile)
|
|
||||||
*/
|
|
||||||
@ItemTypes
|
|
||||||
public final int type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item.
|
|
||||||
*/
|
|
||||||
public final int id;
|
|
||||||
|
|
||||||
public Item(Object entity, @ItemTypes int type, int id) {
|
|
||||||
this.entity = entity;
|
|
||||||
this.type = type;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override it to make comparision in the {@link ItemsDataDiffCallback}
|
|
||||||
*
|
|
||||||
* @param obj object to compared with
|
|
||||||
* @return true if the same object or has equal value.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(obj instanceof Item)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Item targetItem = (Item) obj;
|
|
||||||
if (type != targetItem.type || id != targetItem.id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case TYPE_DASHBOARD_TILE:
|
|
||||||
final Tile localTile = (Tile) entity;
|
|
||||||
final Tile targetTile = (Tile) targetItem.entity;
|
|
||||||
|
|
||||||
// Only check id and summary for dashboard tile
|
|
||||||
return localTile.getId() == targetTile.getId()
|
|
||||||
&& TextUtils.equals(
|
|
||||||
localTile.getSummaryReference(),
|
|
||||||
targetTile.getSummaryReference());
|
|
||||||
case TYPE_SUGGESTION_CONTAINER:
|
|
||||||
case TYPE_CONDITION_CONTAINER:
|
|
||||||
// Fall through to default
|
|
||||||
default:
|
|
||||||
return entity == null ? targetItem.entity == null
|
|
||||||
: entity.equals(targetItem.entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class contains the data needed to build the suggestion/condition header. The data can
|
|
||||||
* also be used to check the diff in DiffUtil.Callback
|
|
||||||
*/
|
|
||||||
public static class ConditionHeaderData {
|
|
||||||
public final List<Drawable> conditionIcons;
|
|
||||||
public final CharSequence title;
|
|
||||||
public final int conditionCount;
|
|
||||||
|
|
||||||
public ConditionHeaderData(List<ConditionalCard> conditions) {
|
|
||||||
conditionCount = sizeOf(conditions);
|
|
||||||
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
|
|
||||||
conditionIcons = new ArrayList<>();
|
|
||||||
if (conditions == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (ConditionalCard card : conditions) {
|
|
||||||
conditionIcons.add(card.getIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -30,6 +30,7 @@ import com.android.settings.deviceinfo.StorageDashboardFragment;
|
|||||||
import com.android.settings.display.NightDisplaySettings;
|
import com.android.settings.display.NightDisplaySettings;
|
||||||
import com.android.settings.fuelgauge.PowerUsageSummary;
|
import com.android.settings.fuelgauge.PowerUsageSummary;
|
||||||
import com.android.settings.gestures.GestureSettings;
|
import com.android.settings.gestures.GestureSettings;
|
||||||
|
import com.android.settings.homepage.TopLevelSettings;
|
||||||
import com.android.settings.language.LanguageAndInputSettings;
|
import com.android.settings.language.LanguageAndInputSettings;
|
||||||
import com.android.settings.network.NetworkDashboardFragment;
|
import com.android.settings.network.NetworkDashboardFragment;
|
||||||
import com.android.settings.notification.ConfigureNotificationSettings;
|
import com.android.settings.notification.ConfigureNotificationSettings;
|
||||||
@@ -61,9 +62,8 @@ public class DashboardFragmentRegistry {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
|
PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
|
||||||
// TODO(b/110405144): Add the mapping when IA.homepage intent-filter is is removed.
|
PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
|
||||||
// PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
|
CategoryKey.CATEGORY_HOMEPAGE);
|
||||||
// CategoryKey.CATEGORY_HOMEPAGE);
|
|
||||||
PARENT_TO_CATEGORY_KEY_MAP.put(
|
PARENT_TO_CATEGORY_KEY_MAP.put(
|
||||||
NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
|
NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
|
||||||
PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
|
PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.dashboard;
|
|
||||||
|
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
|
||||||
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
|
|
||||||
public class DashboardItemAnimator extends DefaultItemAnimator {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY,
|
|
||||||
int toX, int toY) {
|
|
||||||
final Object tag = oldHolder.itemView.getTag();
|
|
||||||
if (tag instanceof Tile && oldHolder == newHolder) {
|
|
||||||
// When this view has other move animation running, skip this value to avoid
|
|
||||||
// animations interrupt each other.
|
|
||||||
if (!isRunning()) {
|
|
||||||
fromX += ViewCompat.getTranslationX(oldHolder.itemView);
|
|
||||||
fromY += ViewCompat.getTranslationY(oldHolder.itemView);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromX == toX && fromY == toY) {
|
|
||||||
dispatchMoveFinished(oldHolder);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,292 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 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.dashboard;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.annotation.WorkerThread;
|
|
||||||
import androidx.loader.app.LoaderManager;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.core.InstrumentedFragment;
|
|
||||||
import com.android.settings.core.SettingsBaseActivity;
|
|
||||||
import com.android.settings.core.SettingsBaseActivity.CategoryListener;
|
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionListener;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionManager;
|
|
||||||
import com.android.settings.homepage.conditional.FocusRecyclerView;
|
|
||||||
import com.android.settings.homepage.conditional.FocusRecyclerView.FocusListener;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settings.widget.ActionBarShadowController;
|
|
||||||
import com.android.settingslib.drawer.CategoryKey;
|
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
|
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecated in favor of {@link com.android.settings.homepage.TopLevelSettings}
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class DashboardSummary extends InstrumentedFragment
|
|
||||||
implements CategoryListener, ConditionListener,
|
|
||||||
FocusListener, SuggestionControllerMixinCompat.SuggestionControllerHost {
|
|
||||||
public static final boolean DEBUG = false;
|
|
||||||
private static final boolean DEBUG_TIMING = false;
|
|
||||||
private static final int MAX_WAIT_MILLIS = 3000;
|
|
||||||
private static final String TAG = "DashboardSummary";
|
|
||||||
|
|
||||||
private static final String STATE_SCROLL_POSITION = "scroll_position";
|
|
||||||
private static final String STATE_CATEGORIES_CHANGE_CALLED = "categories_change_called";
|
|
||||||
|
|
||||||
private final Handler mHandler = new Handler();
|
|
||||||
|
|
||||||
private FocusRecyclerView mDashboard;
|
|
||||||
private DashboardAdapter mAdapter;
|
|
||||||
private SummaryLoader mSummaryLoader;
|
|
||||||
private ConditionManager mConditionManager;
|
|
||||||
private LinearLayoutManager mLayoutManager;
|
|
||||||
private SuggestionControllerMixinCompat mSuggestionControllerMixin;
|
|
||||||
private DashboardFeatureProvider mDashboardFeatureProvider;
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean mIsOnCategoriesChangedCalled;
|
|
||||||
private boolean mOnConditionsChangedCalled;
|
|
||||||
|
|
||||||
private DashboardCategory mStagingCategory;
|
|
||||||
private List<Suggestion> mStagingSuggestions;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMetricsCategory() {
|
|
||||||
return MetricsEvent.DASHBOARD_SUMMARY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
Log.d(TAG, "Creating SuggestionControllerMixinCompat");
|
|
||||||
final SuggestionFeatureProvider suggestionFeatureProvider = FeatureFactory
|
|
||||||
.getFactory(context)
|
|
||||||
.getSuggestionFeatureProvider(context);
|
|
||||||
if (suggestionFeatureProvider.isSuggestionEnabled(context)) {
|
|
||||||
mSuggestionControllerMixin = new SuggestionControllerMixinCompat(
|
|
||||||
context, this /* host */, getSettingsLifecycle(),
|
|
||||||
suggestionFeatureProvider.getSuggestionServiceComponent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LoaderManager getLoaderManager() {
|
|
||||||
if (!isAdded()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return super.getLoaderManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
Log.d(TAG, "Starting DashboardSummary");
|
|
||||||
final Activity activity = getActivity();
|
|
||||||
mDashboardFeatureProvider = FeatureFactory.getFactory(activity)
|
|
||||||
.getDashboardFeatureProvider(activity);
|
|
||||||
|
|
||||||
mSummaryLoader = new SummaryLoader(activity, CategoryKey.CATEGORY_HOMEPAGE);
|
|
||||||
|
|
||||||
mConditionManager =
|
|
||||||
new ConditionManager(
|
|
||||||
activity, this /* listener */);
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
mIsOnCategoriesChangedCalled =
|
|
||||||
savedInstanceState.getBoolean(STATE_CATEGORIES_CHANGE_CALLED);
|
|
||||||
}
|
|
||||||
if (DEBUG_TIMING) {
|
|
||||||
Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
mSummaryLoader.release();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
((SettingsBaseActivity) getActivity()).addCategoryListener(this);
|
|
||||||
mSummaryLoader.setListening(true);
|
|
||||||
mConditionManager.startMonitoringStateChange();
|
|
||||||
if (DEBUG_TIMING) {
|
|
||||||
Log.d(TAG, "onResume took " + (System.currentTimeMillis() - startTime) + " ms");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
((SettingsBaseActivity) getActivity()).remCategoryListener(this);
|
|
||||||
mSummaryLoader.setListening(false);
|
|
||||||
mConditionManager.stopMonitoringStateChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
if (hasWindowFocus) {
|
|
||||||
mConditionManager.startMonitoringStateChange();
|
|
||||||
} else {
|
|
||||||
mConditionManager.stopMonitoringStateChange();
|
|
||||||
}
|
|
||||||
if (DEBUG_TIMING) {
|
|
||||||
Log.d(TAG, "onWindowFocusChanged took "
|
|
||||||
+ (System.currentTimeMillis() - startTime) + " ms");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
if (mLayoutManager == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
outState.putBoolean(STATE_CATEGORIES_CHANGE_CALLED, mIsOnCategoriesChangedCalled);
|
|
||||||
outState.putInt(STATE_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
final View root = inflater.inflate(R.layout.dashboard, container, false);
|
|
||||||
mDashboard = root.findViewById(R.id.dashboard_container);
|
|
||||||
mLayoutManager = new LinearLayoutManager(getContext());
|
|
||||||
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
|
|
||||||
if (bundle != null) {
|
|
||||||
int scrollPosition = bundle.getInt(STATE_SCROLL_POSITION);
|
|
||||||
mLayoutManager.scrollToPosition(scrollPosition);
|
|
||||||
}
|
|
||||||
mDashboard.setLayoutManager(mLayoutManager);
|
|
||||||
mDashboard.setHasFixedSize(true);
|
|
||||||
mDashboard.setListener(this);
|
|
||||||
mDashboard.setItemAnimator(new DashboardItemAnimator());
|
|
||||||
mAdapter = new DashboardAdapter(getContext(), bundle,
|
|
||||||
mConditionManager,
|
|
||||||
mSuggestionControllerMixin,
|
|
||||||
getSettingsLifecycle());
|
|
||||||
mDashboard.setAdapter(mAdapter);
|
|
||||||
mSummaryLoader.setSummaryConsumer(mAdapter);
|
|
||||||
ActionBarShadowController.attachToRecyclerView(
|
|
||||||
getActivity().findViewById(R.id.search_bar_container), getSettingsLifecycle(),
|
|
||||||
mDashboard);
|
|
||||||
rebuildUI();
|
|
||||||
if (DEBUG_TIMING) {
|
|
||||||
Log.d(TAG, "onCreateView took "
|
|
||||||
+ (System.currentTimeMillis() - startTime) + " ms");
|
|
||||||
}
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void rebuildUI() {
|
|
||||||
ThreadUtils.postOnBackgroundThread(() -> updateCategory());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCategoriesChanged() {
|
|
||||||
// Bypass rebuildUI() on the first call of onCategoriesChanged, since rebuildUI() happens
|
|
||||||
// in onViewCreated as well when app starts. But, on the subsequent calls we need to
|
|
||||||
// rebuildUI() because there might be some changes to suggestions and categories.
|
|
||||||
if (mIsOnCategoriesChangedCalled) {
|
|
||||||
rebuildUI();
|
|
||||||
}
|
|
||||||
mIsOnCategoriesChangedCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConditionsChanged() {
|
|
||||||
Log.d(TAG, "onConditionsChanged");
|
|
||||||
// Bypass refreshing the conditions on the first call of onConditionsChanged.
|
|
||||||
// onConditionsChanged is called immediately everytime we start listening to the conditions
|
|
||||||
// change when we gain window focus. Since the conditions are passed to the adapter's
|
|
||||||
// constructor when we create the view, the first handling is not necessary.
|
|
||||||
// But, on the subsequent calls we need to handle it because there might be real changes to
|
|
||||||
// conditions.
|
|
||||||
if (mOnConditionsChangedCalled) {
|
|
||||||
final boolean scrollToTop =
|
|
||||||
mLayoutManager.findFirstCompletelyVisibleItemPosition() <= 1;
|
|
||||||
mAdapter.setConditions(mConditionManager.getDisplayableCards());
|
|
||||||
|
|
||||||
if (scrollToTop) {
|
|
||||||
mDashboard.scrollToPosition(0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mOnConditionsChangedCalled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuggestionReady(List<Suggestion> suggestions) {
|
|
||||||
mStagingSuggestions = suggestions;
|
|
||||||
mAdapter.setSuggestions(suggestions);
|
|
||||||
if (mStagingCategory != null) {
|
|
||||||
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
|
|
||||||
mHandler.removeCallbacksAndMessages(null);
|
|
||||||
mAdapter.setCategory(mStagingCategory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
void updateCategory() {
|
|
||||||
final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
|
|
||||||
CategoryKey.CATEGORY_HOMEPAGE);
|
|
||||||
mSummaryLoader.updateSummaryToCache(category);
|
|
||||||
mStagingCategory = category;
|
|
||||||
if (mSuggestionControllerMixin == null) {
|
|
||||||
ThreadUtils.postOnMainThread(() -> mAdapter.setCategory(mStagingCategory));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mSuggestionControllerMixin.isSuggestionLoaded()) {
|
|
||||||
Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
|
|
||||||
ThreadUtils.postOnMainThread(() -> {
|
|
||||||
if (mStagingSuggestions != null) {
|
|
||||||
mAdapter.setSuggestions(mStagingSuggestions);
|
|
||||||
}
|
|
||||||
mAdapter.setCategory(mStagingCategory);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "Suggestion NOT loaded, delaying setCategory by " + MAX_WAIT_MILLIS + "ms");
|
|
||||||
mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -44,7 +44,7 @@ import java.lang.reflect.Field;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SummaryLoader {
|
public class SummaryLoader {
|
||||||
private static final boolean DEBUG = DashboardSummary.DEBUG;
|
private static final boolean DEBUG = false;
|
||||||
private static final String TAG = "SummaryLoader";
|
private static final String TAG = "SummaryLoader";
|
||||||
|
|
||||||
public static final String SUMMARY_PROVIDER_FACTORY = "SUMMARY_PROVIDER_FACTORY";
|
public static final String SUMMARY_PROVIDER_FACTORY = "SUMMARY_PROVIDER_FACTORY";
|
||||||
|
@@ -1,277 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 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.dashboard.suggestions;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.Utils;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
|
|
||||||
import com.android.settingslib.utils.IconCache;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> implements
|
|
||||||
LifecycleObserver, OnSaveInstanceState {
|
|
||||||
public static final String TAG = "SuggestionAdapter";
|
|
||||||
|
|
||||||
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
|
|
||||||
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private final IconCache mCache;
|
|
||||||
private final ArrayList<String> mSuggestionsShownLogged;
|
|
||||||
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
|
|
||||||
private final SuggestionControllerMixinCompat mSuggestionControllerMixin;
|
|
||||||
private final Callback mCallback;
|
|
||||||
private final CardConfig mConfig;
|
|
||||||
|
|
||||||
private List<Suggestion> mSuggestions;
|
|
||||||
|
|
||||||
public interface Callback {
|
|
||||||
/**
|
|
||||||
* Called when the close button of the suggestion card is clicked.
|
|
||||||
*/
|
|
||||||
void onSuggestionClosed(Suggestion suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SuggestionAdapter(Context context,
|
|
||||||
SuggestionControllerMixinCompat suggestionControllerMixin, Bundle savedInstanceState,
|
|
||||||
Callback callback, Lifecycle lifecycle) {
|
|
||||||
mContext = context;
|
|
||||||
mSuggestionControllerMixin = suggestionControllerMixin;
|
|
||||||
mCache = new IconCache(context);
|
|
||||||
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
|
||||||
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
|
||||||
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
|
|
||||||
mCallback = callback;
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
|
|
||||||
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
|
|
||||||
STATE_SUGGESTIONS_SHOWN_LOGGED);
|
|
||||||
} else {
|
|
||||||
mSuggestionsShownLogged = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lifecycle != null) {
|
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
|
||||||
mConfig = CardConfig.get(context);
|
|
||||||
|
|
||||||
setHasStableIds(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
|
|
||||||
viewType, parent, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
|
||||||
final Suggestion suggestion = mSuggestions.get(position);
|
|
||||||
final String id = suggestion.getId();
|
|
||||||
final int suggestionCount = mSuggestions.size();
|
|
||||||
if (!mSuggestionsShownLogged.contains(id)) {
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
|
|
||||||
mSuggestionsShownLogged.add(id);
|
|
||||||
}
|
|
||||||
final Icon icon = suggestion.getIcon();
|
|
||||||
final Drawable drawable = mCache.getIcon(icon);
|
|
||||||
if (drawable != null && (suggestion.getFlags() & Suggestion.FLAG_ICON_TINTABLE) != 0) {
|
|
||||||
drawable.setTintList(Utils.getColorAccent(mContext));
|
|
||||||
}
|
|
||||||
holder.icon.setImageDrawable(drawable);
|
|
||||||
holder.title.setText(suggestion.getTitle());
|
|
||||||
holder.title.setTypeface(Typeface.create(
|
|
||||||
mContext.getString(com.android.internal.R.string.config_headlineFontFamily),
|
|
||||||
Typeface.NORMAL));
|
|
||||||
|
|
||||||
if (suggestionCount == 1) {
|
|
||||||
final CharSequence summary = suggestion.getSummary();
|
|
||||||
if (!TextUtils.isEmpty(summary)) {
|
|
||||||
holder.summary.setText(summary);
|
|
||||||
holder.summary.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.summary.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mConfig.setCardLayout(holder, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
final View closeButton = holder.itemView.findViewById(R.id.close_button);
|
|
||||||
if (closeButton != null) {
|
|
||||||
closeButton.setOnClickListener(v -> {
|
|
||||||
mSuggestionFeatureProvider.dismissSuggestion(
|
|
||||||
mContext, mSuggestionControllerMixin, suggestion);
|
|
||||||
if (mCallback != null) {
|
|
||||||
mCallback.onSuggestionClosed(suggestion);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
View clickHandler = holder.itemView;
|
|
||||||
// If a view with @android:id/primary is defined, use that as the click handler
|
|
||||||
// instead.
|
|
||||||
final View primaryAction = holder.itemView.findViewById(android.R.id.primary);
|
|
||||||
if (primaryAction != null) {
|
|
||||||
clickHandler = primaryAction;
|
|
||||||
}
|
|
||||||
clickHandler.setOnClickListener(v -> {
|
|
||||||
mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_SUGGESTION, id);
|
|
||||||
try {
|
|
||||||
suggestion.getPendingIntent().send();
|
|
||||||
mSuggestionControllerMixin.launchSuggestion(suggestion);
|
|
||||||
} catch (PendingIntent.CanceledException e) {
|
|
||||||
Log.w(TAG, "Failed to start suggestion " + suggestion.getTitle());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return Objects.hash(mSuggestions.get(position).getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
final Suggestion suggestion = getSuggestion(position);
|
|
||||||
if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
|
|
||||||
return R.layout.suggestion_tile_with_button;
|
|
||||||
}
|
|
||||||
if (getItemCount() == 1) {
|
|
||||||
return R.layout.suggestion_tile;
|
|
||||||
}
|
|
||||||
return R.layout.suggestion_tile_two_cards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mSuggestions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Suggestion getSuggestion(int position) {
|
|
||||||
final long itemId = getItemId(position);
|
|
||||||
if (mSuggestions == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (Suggestion suggestion : mSuggestions) {
|
|
||||||
if (Objects.hash(suggestion.getId()) == itemId) {
|
|
||||||
return suggestion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeSuggestion(Suggestion suggestion) {
|
|
||||||
final int position = mSuggestions.indexOf(suggestion);
|
|
||||||
mSuggestions.remove(suggestion);
|
|
||||||
notifyItemRemoved(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
if (mSuggestions != null) {
|
|
||||||
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
|
|
||||||
new ArrayList<>(mSuggestions));
|
|
||||||
}
|
|
||||||
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSuggestions(List<Suggestion> suggestions) {
|
|
||||||
mSuggestions = suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Suggestion> getSuggestions() {
|
|
||||||
return mSuggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static class CardConfig {
|
|
||||||
// Card start/end margin
|
|
||||||
private final int mMarginInner;
|
|
||||||
private final int mMarginOuter;
|
|
||||||
private final WindowManager mWindowManager;
|
|
||||||
|
|
||||||
private static CardConfig sConfig;
|
|
||||||
|
|
||||||
private CardConfig(Context context) {
|
|
||||||
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
|
||||||
final Resources res = context.getResources();
|
|
||||||
mMarginInner =
|
|
||||||
res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
|
|
||||||
mMarginOuter =
|
|
||||||
res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CardConfig get(Context context) {
|
|
||||||
if (sConfig == null) {
|
|
||||||
sConfig = new CardConfig(context);
|
|
||||||
}
|
|
||||||
return sConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setCardLayout(DashboardItemHolder holder, int position) {
|
|
||||||
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
getWidthForTwoCrads(), LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
params.setMarginStart(position == 0 ? mMarginOuter : mMarginInner);
|
|
||||||
params.setMarginEnd(position != 0 ? mMarginOuter : 0);
|
|
||||||
holder.itemView.setLayoutParams(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getWidthForTwoCrads() {
|
|
||||||
return (getScreenWidth() - mMarginInner - mMarginOuter * 2) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
int getScreenWidth() {
|
|
||||||
final DisplayMetrics metrics = new DisplayMetrics();
|
|
||||||
mWindowManager.getDefaultDisplay().getMetrics(metrics);
|
|
||||||
return metrics.widthPixels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -51,7 +51,6 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
import com.android.settings.dashboard.SummaryLoader;
|
|
||||||
import com.android.settings.search.BaseSearchIndexProvider;
|
import com.android.settings.search.BaseSearchIndexProvider;
|
||||||
import com.android.settings.search.Indexable;
|
import com.android.settings.search.Indexable;
|
||||||
import com.android.settings.search.SearchIndexableRaw;
|
import com.android.settings.search.SearchIndexableRaw;
|
||||||
@@ -61,7 +60,6 @@ import com.android.settingslib.deviceinfo.PrivateStorageInfo;
|
|||||||
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
|
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
|
||||||
import com.android.settingslib.search.SearchIndexable;
|
import com.android.settingslib.search.SearchIndexable;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -542,41 +540,6 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
|
|
||||||
private final Context mContext;
|
|
||||||
private final SummaryLoader mLoader;
|
|
||||||
private final StorageManagerVolumeProvider mStorageManagerVolumeProvider;
|
|
||||||
|
|
||||||
private SummaryProvider(Context context, SummaryLoader loader) {
|
|
||||||
mContext = context;
|
|
||||||
mLoader = loader;
|
|
||||||
final StorageManager storageManager = mContext.getSystemService(StorageManager.class);
|
|
||||||
mStorageManagerVolumeProvider = new StorageManagerVolumeProvider(storageManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setListening(boolean listening) {
|
|
||||||
if (listening) {
|
|
||||||
updateSummary();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSummary() {
|
|
||||||
// TODO: Register listener.
|
|
||||||
final NumberFormat percentageFormat = NumberFormat.getPercentInstance();
|
|
||||||
final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(
|
|
||||||
mStorageManagerVolumeProvider);
|
|
||||||
double privateUsedBytes = info.totalBytes - info.freeBytes;
|
|
||||||
mLoader.setSummary(this, mContext.getString(R.string.storage_summary,
|
|
||||||
percentageFormat.format(privateUsedBytes / info.totalBytes),
|
|
||||||
Formatter.formatFileSize(mContext, info.freeBytes)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
|
|
||||||
= (activity, summaryLoader) -> new SummaryProvider(activity, summaryLoader);
|
|
||||||
|
|
||||||
/** Enable indexing of searchable data */
|
/** Enable indexing of searchable data */
|
||||||
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
new BaseSearchIndexProvider() {
|
new BaseSearchIndexProvider() {
|
||||||
|
@@ -17,9 +17,7 @@
|
|||||||
package com.android.settings.fuelgauge;
|
package com.android.settings.fuelgauge;
|
||||||
|
|
||||||
import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType;
|
import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType;
|
||||||
import static com.android.settings.fuelgauge.TopLevelBatteryPreferenceController.getDashboardLabel;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.BatteryStats;
|
import android.os.BatteryStats;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -43,7 +41,6 @@ import com.android.settings.SettingsActivity;
|
|||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.applications.LayoutPreference;
|
import com.android.settings.applications.LayoutPreference;
|
||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
import com.android.settings.dashboard.SummaryLoader;
|
|
||||||
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
|
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
|
||||||
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
|
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
|
||||||
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
|
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
|
||||||
@@ -389,35 +386,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
|
|||||||
restartBatteryTipLoader();
|
restartBatteryTipLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
|
|
||||||
private final Context mContext;
|
|
||||||
private final SummaryLoader mLoader;
|
|
||||||
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
|
|
||||||
|
|
||||||
private SummaryProvider(Context context, SummaryLoader loader) {
|
|
||||||
mContext = context;
|
|
||||||
mLoader = loader;
|
|
||||||
mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext);
|
|
||||||
mBatteryBroadcastReceiver.setBatteryChangedListener(type -> {
|
|
||||||
BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onBatteryInfoLoaded(BatteryInfo info) {
|
|
||||||
mLoader.setSummary(SummaryProvider.this, getDashboardLabel(mContext, info));
|
|
||||||
}
|
|
||||||
}, true /* shortString */);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setListening(boolean listening) {
|
|
||||||
if (listening) {
|
|
||||||
mBatteryBroadcastReceiver.register();
|
|
||||||
} else {
|
|
||||||
mBatteryBroadcastReceiver.unRegister();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
new BaseSearchIndexProvider() {
|
new BaseSearchIndexProvider() {
|
||||||
@@ -429,13 +397,4 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
|
|||||||
return Collections.singletonList(sir);
|
return Collections.singletonList(sir);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
|
|
||||||
= new SummaryLoader.SummaryProviderFactory() {
|
|
||||||
@Override
|
|
||||||
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
|
|
||||||
SummaryLoader summaryLoader) {
|
|
||||||
return new SummaryProvider(activity, summaryLoader);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@@ -1,130 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.homepage.conditional;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Button;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapter;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ConditionAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder> {
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private final ConditionManager mConditionManager;
|
|
||||||
private final List<ConditionalCard> mConditions;
|
|
||||||
private final boolean mExpanded;
|
|
||||||
|
|
||||||
public ConditionAdapter(Context context, ConditionManager conditionManager,
|
|
||||||
List<ConditionalCard> conditions, boolean expanded) {
|
|
||||||
mContext = context;
|
|
||||||
mConditionManager = conditionManager;
|
|
||||||
mConditions = conditions;
|
|
||||||
mExpanded = expanded;
|
|
||||||
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
|
||||||
|
|
||||||
setHasStableIds(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DashboardAdapter.DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
return new DashboardAdapter.DashboardItemHolder(LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(viewType, parent, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(DashboardAdapter.DashboardItemHolder holder, int position) {
|
|
||||||
final ConditionalCard condition = mConditions.get(position);
|
|
||||||
final boolean isLastItem = position == mConditions.size() - 1;
|
|
||||||
bindViews(condition, holder, isLastItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return mConditions.get(position).getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
return R.layout.condition_tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
if (mExpanded) {
|
|
||||||
return mConditions.size();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindViews(final ConditionalCard condition,
|
|
||||||
DashboardAdapter.DashboardItemHolder view, boolean isLastItem) {
|
|
||||||
mMetricsFeatureProvider.visible(mContext, MetricsProto.MetricsEvent.DASHBOARD_SUMMARY,
|
|
||||||
condition.getMetricsConstant());
|
|
||||||
view.itemView.findViewById(R.id.content).setOnClickListener(
|
|
||||||
v -> {
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
|
||||||
MetricsProto.MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
|
|
||||||
condition.getMetricsConstant());
|
|
||||||
mConditionManager.onPrimaryClick(mContext, condition.getId());
|
|
||||||
});
|
|
||||||
view.icon.setImageDrawable(condition.getIcon());
|
|
||||||
view.title.setText(condition.getTitle());
|
|
||||||
view.summary.setText(condition.getSummary());
|
|
||||||
|
|
||||||
setViewVisibility(view.itemView, R.id.divider, !isLastItem);
|
|
||||||
|
|
||||||
final CharSequence action = condition.getActionText();
|
|
||||||
final boolean hasButtons = !TextUtils.isEmpty(action);
|
|
||||||
setViewVisibility(view.itemView, R.id.buttonBar, hasButtons);
|
|
||||||
|
|
||||||
final Button button = view.itemView.findViewById(R.id.first_action);
|
|
||||||
if (hasButtons) {
|
|
||||||
button.setVisibility(View.VISIBLE);
|
|
||||||
button.setText(action);
|
|
||||||
button.setOnClickListener(v -> {
|
|
||||||
final Context context = v.getContext();
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
context, MetricsProto.MetricsEvent.ACTION_SETTINGS_CONDITION_BUTTON,
|
|
||||||
condition.getMetricsConstant());
|
|
||||||
mConditionManager.onActionClick(condition.getId());
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
button.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setViewVisibility(View containerView, int viewId, boolean visible) {
|
|
||||||
View view = containerView.findViewById(viewId);
|
|
||||||
if (view != null) {
|
|
||||||
view.setVisibility(visible ? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -15,29 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.settings.network;
|
package com.android.settings.network;
|
||||||
|
|
||||||
import static android.provider.Settings.ACTION_DATA_USAGE_SETTINGS;
|
|
||||||
import static com.android.settings.network.MobilePlanPreferenceController
|
import static com.android.settings.network.MobilePlanPreferenceController
|
||||||
.MANAGE_MOBILE_PLAN_DIALOG_ID;
|
.MANAGE_MOBILE_PLAN_DIALOG_ID;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.icu.text.ListFormatter;
|
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
import android.text.BidiFormatter;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto;
|
import com.android.internal.logging.nano.MetricsProto;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.dashboard.SummaryLoader;
|
|
||||||
import com.android.settings.network.MobilePlanPreferenceController.MobilePlanPreferenceHost;
|
import com.android.settings.network.MobilePlanPreferenceController.MobilePlanPreferenceHost;
|
||||||
import com.android.settings.search.BaseSearchIndexProvider;
|
import com.android.settings.search.BaseSearchIndexProvider;
|
||||||
import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
|
import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
|
||||||
@@ -48,7 +39,6 @@ import com.android.settingslib.search.SearchIndexable;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.function.BooleanSupplier;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SearchIndexable
|
@SearchIndexable
|
||||||
@@ -153,84 +143,6 @@ public class NetworkDashboardFragment extends DashboardFragment implements
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/110405144): Remove SummaryProvider
|
|
||||||
@VisibleForTesting
|
|
||||||
static class SummaryProvider implements SummaryLoader.SummaryProvider {
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final SummaryLoader mSummaryLoader;
|
|
||||||
private final WifiMasterSwitchPreferenceController mWifiPreferenceController;
|
|
||||||
private final MobileNetworkPreferenceController mMobileNetworkPreferenceController;
|
|
||||||
private final TetherPreferenceController mTetherPreferenceController;
|
|
||||||
private final BooleanSupplier mHasDataUsageActivity;
|
|
||||||
|
|
||||||
public SummaryProvider(Context context, SummaryLoader summaryLoader) {
|
|
||||||
this(context, summaryLoader,
|
|
||||||
new WifiMasterSwitchPreferenceController(context, null),
|
|
||||||
new MobileNetworkPreferenceController(context),
|
|
||||||
new TetherPreferenceController(context, null /* lifecycle */),
|
|
||||||
() -> {
|
|
||||||
final Intent intent = new Intent(ACTION_DATA_USAGE_SETTINGS);
|
|
||||||
final PackageManager pm = context.getPackageManager();
|
|
||||||
return intent.resolveActivity(pm) != null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
|
||||||
SummaryProvider(Context context, SummaryLoader summaryLoader,
|
|
||||||
WifiMasterSwitchPreferenceController wifiPreferenceController,
|
|
||||||
MobileNetworkPreferenceController mobileNetworkPreferenceController,
|
|
||||||
TetherPreferenceController tetherPreferenceController,
|
|
||||||
BooleanSupplier hasDataUsageActivity) {
|
|
||||||
mContext = context;
|
|
||||||
mSummaryLoader = summaryLoader;
|
|
||||||
mWifiPreferenceController = wifiPreferenceController;
|
|
||||||
mMobileNetworkPreferenceController = mobileNetworkPreferenceController;
|
|
||||||
mTetherPreferenceController = tetherPreferenceController;
|
|
||||||
mHasDataUsageActivity = hasDataUsageActivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setListening(boolean listening) {
|
|
||||||
if (listening) {
|
|
||||||
final String wifiSummary = BidiFormatter.getInstance()
|
|
||||||
.unicodeWrap(mContext.getString(R.string.wifi_settings_title));
|
|
||||||
final String mobileSummary = mContext.getString(
|
|
||||||
R.string.network_dashboard_summary_mobile);
|
|
||||||
final String dataUsageSummary = mContext.getString(
|
|
||||||
R.string.network_dashboard_summary_data_usage);
|
|
||||||
final String hotspotSummary = mContext.getString(
|
|
||||||
R.string.network_dashboard_summary_hotspot);
|
|
||||||
|
|
||||||
final List<String> summaries = new ArrayList<>();
|
|
||||||
if (mWifiPreferenceController.isAvailable() && !TextUtils.isEmpty(wifiSummary)) {
|
|
||||||
summaries.add(wifiSummary);
|
|
||||||
}
|
|
||||||
if (mMobileNetworkPreferenceController.isAvailable() && !TextUtils.isEmpty(mobileSummary)) {
|
|
||||||
summaries.add(mobileSummary);
|
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(dataUsageSummary) && mHasDataUsageActivity.getAsBoolean()) {
|
|
||||||
summaries.add(dataUsageSummary);
|
|
||||||
}
|
|
||||||
if (mTetherPreferenceController.isAvailable() && !TextUtils.isEmpty(hotspotSummary)) {
|
|
||||||
summaries.add(hotspotSummary);
|
|
||||||
}
|
|
||||||
mSummaryLoader.setSummary(this, ListFormatter.getInstance().format(summaries));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
|
|
||||||
= new SummaryLoader.SummaryProviderFactory() {
|
|
||||||
@Override
|
|
||||||
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
|
|
||||||
SummaryLoader summaryLoader) {
|
|
||||||
return new SummaryProvider(activity, summaryLoader);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
new BaseSearchIndexProvider() {
|
new BaseSearchIndexProvider() {
|
||||||
@Override
|
@Override
|
||||||
|
@@ -18,21 +18,16 @@ package com.android.settings.security;
|
|||||||
import static com.android.settings.security.EncryptionStatusPreferenceController
|
import static com.android.settings.security.EncryptionStatusPreferenceController
|
||||||
.PREF_KEY_ENCRYPTION_SECURITY_PAGE;
|
.PREF_KEY_ENCRYPTION_SECURITY_PAGE;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.hardware.face.FaceManager;
|
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto;
|
import com.android.internal.logging.nano.MetricsProto;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
|
||||||
import com.android.settings.biometrics.face.FaceStatusPreferenceController;
|
import com.android.settings.biometrics.face.FaceStatusPreferenceController;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintProfileStatusPreferenceController;
|
import com.android.settings.biometrics.fingerprint.FingerprintProfileStatusPreferenceController;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
|
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.dashboard.SummaryLoader;
|
|
||||||
import com.android.settings.enterprise.EnterprisePrivacyPreferenceController;
|
import com.android.settings.enterprise.EnterprisePrivacyPreferenceController;
|
||||||
import com.android.settings.location.LocationPreferenceController;
|
import com.android.settings.location.LocationPreferenceController;
|
||||||
import com.android.settings.search.BaseSearchIndexProvider;
|
import com.android.settings.search.BaseSearchIndexProvider;
|
||||||
@@ -166,44 +161,4 @@ public class SecuritySettings extends DashboardFragment {
|
|||||||
null /* host*/);
|
null /* host*/);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static class SummaryProvider implements SummaryLoader.SummaryProvider {
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final SummaryLoader mSummaryLoader;
|
|
||||||
|
|
||||||
public SummaryProvider(Context context, SummaryLoader summaryLoader) {
|
|
||||||
mContext = context;
|
|
||||||
mSummaryLoader = summaryLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setListening(boolean listening) {
|
|
||||||
if (listening) {
|
|
||||||
final FingerprintManager fpm =
|
|
||||||
Utils.getFingerprintManagerOrNull(mContext);
|
|
||||||
final FaceManager faceManager =
|
|
||||||
Utils.getFaceManagerOrNull(mContext);
|
|
||||||
if (faceManager != null && faceManager.isHardwareDetected()) {
|
|
||||||
mSummaryLoader.setSummary(this,
|
|
||||||
mContext.getString(R.string.security_dashboard_summary_face));
|
|
||||||
} else if (fpm != null && fpm.isHardwareDetected()) {
|
|
||||||
mSummaryLoader.setSummary(this,
|
|
||||||
mContext.getString(R.string.security_dashboard_summary));
|
|
||||||
} else {
|
|
||||||
mSummaryLoader.setSummary(this, mContext.getString(
|
|
||||||
R.string.security_dashboard_summary_no_fingerprint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY =
|
|
||||||
new SummaryLoader.SummaryProviderFactory() {
|
|
||||||
@Override
|
|
||||||
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
|
|
||||||
SummaryLoader summaryLoader) {
|
|
||||||
return new SummaryProvider(activity, summaryLoader);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@@ -1,282 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.dashboard;
|
|
||||||
|
|
||||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.reset;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionManager;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionalCard;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
|
||||||
import com.android.settings.widget.RoundedHomepageIcon;
|
|
||||||
import com.android.settingslib.drawer.CategoryKey;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
import com.android.settingslib.utils.IconCache;
|
|
||||||
|
|
||||||
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.RuntimeEnvironment;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
@Config(shadows = SettingsShadowResources.SettingsShadowTheme.class)
|
|
||||||
public class DashboardAdapterTest {
|
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
|
||||||
private SettingsActivity mContext;
|
|
||||||
@Mock
|
|
||||||
private ConditionalCard mCondition;
|
|
||||||
@Mock
|
|
||||||
private Resources mResources;
|
|
||||||
@Mock
|
|
||||||
private WindowManager mWindowManager;
|
|
||||||
@Mock
|
|
||||||
private ConditionManager mConditionManager;
|
|
||||||
|
|
||||||
private ActivityInfo mActivityInfo;
|
|
||||||
private DashboardAdapter mDashboardAdapter;
|
|
||||||
private List<ConditionalCard> mConditionList;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
FakeFeatureFactory.setupForTest();
|
|
||||||
mActivityInfo = new ActivityInfo();
|
|
||||||
mActivityInfo.packageName = "pkg";
|
|
||||||
mActivityInfo.name = "class";
|
|
||||||
mActivityInfo.metaData = new Bundle();
|
|
||||||
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_TITLE, "test-title");
|
|
||||||
|
|
||||||
when(mContext.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mWindowManager);
|
|
||||||
when(mContext.getResources()).thenReturn(mResources);
|
|
||||||
when(mResources.getQuantityString(any(int.class), any(int.class), any())).thenReturn("");
|
|
||||||
|
|
||||||
mConditionList = new ArrayList<>();
|
|
||||||
mConditionList.add(mCondition);
|
|
||||||
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
|
|
||||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onSuggestionClosed_notOnlySuggestion_updateSuggestionOnly() {
|
|
||||||
final DashboardAdapter adapter =
|
|
||||||
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
|
||||||
mConditionManager,
|
|
||||||
null /* suggestionControllerMixin */,
|
|
||||||
null /* lifecycle */));
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1", "pkg2", "pkg3");
|
|
||||||
adapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
final RecyclerView data = mock(RecyclerView.class);
|
|
||||||
when(data.getResources()).thenReturn(mResources);
|
|
||||||
when(data.getContext()).thenReturn(mContext);
|
|
||||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
|
||||||
final View itemView = mock(View.class);
|
|
||||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
|
||||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
|
||||||
when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
|
|
||||||
final DashboardAdapter.SuggestionContainerHolder holder =
|
|
||||||
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
|
||||||
|
|
||||||
adapter.onBindSuggestion(holder, 0);
|
|
||||||
|
|
||||||
reset(adapter); // clear interactions tracking
|
|
||||||
|
|
||||||
final Suggestion suggestionToRemove = suggestions.get(1);
|
|
||||||
adapter.onSuggestionClosed(suggestionToRemove);
|
|
||||||
|
|
||||||
assertThat(suggestions.size()).isEqualTo(2);
|
|
||||||
assertThat(suggestions.contains(suggestionToRemove)).isFalse();
|
|
||||||
verify(adapter).notifyDashboardDataChanged(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onSuggestionClosed_onlySuggestion_updateDashboardData() {
|
|
||||||
final DashboardAdapter adapter =
|
|
||||||
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
|
||||||
mConditionManager,
|
|
||||||
null /* suggestionControllerMixin */, null /* lifecycle */));
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
adapter.setSuggestions(suggestions);
|
|
||||||
final DashboardData dashboardData = adapter.mDashboardData;
|
|
||||||
reset(adapter); // clear interactions tracking
|
|
||||||
|
|
||||||
adapter.onSuggestionClosed(suggestions.get(0));
|
|
||||||
|
|
||||||
assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
|
|
||||||
verify(adapter).notifyDashboardDataChanged(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onSuggestionClosed_notInSuggestionList_shouldNotUpdateSuggestionList() {
|
|
||||||
final DashboardAdapter adapter =
|
|
||||||
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
|
||||||
mConditionManager,
|
|
||||||
null /* suggestionControllerMixin */, null /* lifecycle */));
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
adapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
reset(adapter); // clear interactions tracking
|
|
||||||
|
|
||||||
adapter.onSuggestionClosed(mock(Suggestion.class));
|
|
||||||
|
|
||||||
verify(adapter, never()).setSuggestions(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
|
|
||||||
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
|
|
||||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
|
|
||||||
mDashboardAdapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
final RecyclerView data = mock(RecyclerView.class);
|
|
||||||
when(data.getResources()).thenReturn(mResources);
|
|
||||||
when(data.getContext()).thenReturn(mContext);
|
|
||||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
|
||||||
final View itemView = mock(View.class);
|
|
||||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
|
||||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
|
||||||
when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
|
|
||||||
final DashboardAdapter.SuggestionContainerHolder holder =
|
|
||||||
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
|
||||||
|
|
||||||
mDashboardAdapter.onBindSuggestion(holder, 0);
|
|
||||||
|
|
||||||
verify(data).setAdapter(any(SuggestionAdapter.class));
|
|
||||||
// should not crash
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindTile_internalTile_shouldNotUseGenericBackgroundIcon() {
|
|
||||||
final Context context = RuntimeEnvironment.application;
|
|
||||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
|
||||||
final DashboardAdapter.DashboardItemHolder holder =
|
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
|
||||||
final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
|
||||||
doReturn(Icon.createWithResource(context, R.drawable.ic_settings))
|
|
||||||
.when(tile).getIcon(context);
|
|
||||||
final IconCache iconCache = mock(IconCache.class);
|
|
||||||
when(iconCache.getIcon(tile.getIcon(context)))
|
|
||||||
.thenReturn(context.getDrawable(R.drawable.ic_settings));
|
|
||||||
|
|
||||||
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
|
||||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
|
||||||
mDashboardAdapter.onBindTile(holder, tile);
|
|
||||||
|
|
||||||
verify(iconCache, never()).updateIcon(any(Icon.class), any(Drawable.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindTile_externalTile_shouldUpdateIcon() {
|
|
||||||
final Context context = spy(RuntimeEnvironment.application);
|
|
||||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
|
||||||
final DashboardAdapter.DashboardItemHolder holder =
|
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
|
||||||
final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
|
||||||
final Icon icon = Icon.createWithResource(context, R.drawable.ic_settings);
|
|
||||||
doReturn(icon).when(tile).getIcon(context);
|
|
||||||
|
|
||||||
final IconCache iconCache = new IconCache(context);
|
|
||||||
|
|
||||||
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
|
||||||
mConditionManager,
|
|
||||||
null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
|
||||||
|
|
||||||
doReturn("another.package").when(context).getPackageName();
|
|
||||||
mDashboardAdapter.onBindTile(holder, tile);
|
|
||||||
|
|
||||||
assertThat(iconCache.getIcon(tile.getIcon(context)))
|
|
||||||
.isInstanceOf(RoundedHomepageIcon.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindTile_externalTile_usingRoundedHomepageIcon_shouldNotUpdateIcon() {
|
|
||||||
final Context context = RuntimeEnvironment.application;
|
|
||||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
|
||||||
final DashboardAdapter.DashboardItemHolder holder =
|
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
|
||||||
final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
|
||||||
doReturn(mock(Icon.class)).when(tile).getIcon(context);
|
|
||||||
when(tile.getIcon(context).getResPackage()).thenReturn("another.package");
|
|
||||||
|
|
||||||
final IconCache iconCache = mock(IconCache.class);
|
|
||||||
when(iconCache.getIcon(tile.getIcon(context))).thenReturn(mock(RoundedHomepageIcon.class));
|
|
||||||
|
|
||||||
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
|
||||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
|
||||||
|
|
||||||
mDashboardAdapter.onBindTile(holder, tile);
|
|
||||||
|
|
||||||
verify(iconCache, never()).updateIcon(eq(tile.getIcon(context)),
|
|
||||||
any(RoundedHomepageIcon.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Suggestion> makeSuggestions(String... pkgNames) {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
for (String pkgName : pkgNames) {
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder(pkgName)
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.build();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
}
|
|
||||||
return suggestions;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,417 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.dashboard;
|
|
||||||
|
|
||||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_CONTAINER;
|
|
||||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_FOOTER;
|
|
||||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_DIVIDER;
|
|
||||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONTAINER;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
|
||||||
import androidx.recyclerview.widget.ListUpdateCallback;
|
|
||||||
|
|
||||||
import com.android.settings.homepage.conditional.AirplaneModeConditionCard;
|
|
||||||
import com.android.settings.homepage.conditional.ConditionalCard;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settingslib.drawer.CategoryKey;
|
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
public class DashboardDataTest {
|
|
||||||
|
|
||||||
private static final String TEST_SUGGESTION_TITLE = "Use fingerprint";
|
|
||||||
private static final int TEST_TILE_ID = 12345;
|
|
||||||
|
|
||||||
private DashboardData mDashboardDataWithOneConditions;
|
|
||||||
private DashboardData mDashboardDataWithTwoConditions;
|
|
||||||
private DashboardData mDashboardDataWithNoItems;
|
|
||||||
private DashboardCategory mDashboardCategory;
|
|
||||||
@Mock
|
|
||||||
private Tile mTestCategoryTile;
|
|
||||||
@Mock
|
|
||||||
private ConditionalCard mTestCondition;
|
|
||||||
@Mock
|
|
||||||
private ConditionalCard mSecondCondition; // condition used to test insert in DiffUtil
|
|
||||||
private Suggestion mTestSuggestion;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void SetUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
|
|
||||||
mDashboardCategory = new DashboardCategory(CategoryKey.CATEGORY_HOMEPAGE);
|
|
||||||
|
|
||||||
// Build suggestions
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
mTestSuggestion = new Suggestion.Builder("pkg")
|
|
||||||
.setTitle(TEST_SUGGESTION_TITLE)
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.build();
|
|
||||||
suggestions.add(mTestSuggestion);
|
|
||||||
|
|
||||||
// Build oneItemConditions
|
|
||||||
final List<ConditionalCard> oneItemConditions = new ArrayList<>();
|
|
||||||
oneItemConditions.add(mTestCondition);
|
|
||||||
|
|
||||||
// Build twoItemConditions
|
|
||||||
final List<ConditionalCard> twoItemsConditions = new ArrayList<>();
|
|
||||||
twoItemsConditions.add(mTestCondition);
|
|
||||||
twoItemsConditions.add(mSecondCondition);
|
|
||||||
|
|
||||||
// Build category
|
|
||||||
when(mTestCategoryTile.getId()).thenReturn(TEST_TILE_ID);
|
|
||||||
|
|
||||||
mDashboardCategory.addTile(mTestCategoryTile);
|
|
||||||
|
|
||||||
// Build DashboardData
|
|
||||||
mDashboardDataWithOneConditions = new DashboardData.Builder()
|
|
||||||
.setConditions(oneItemConditions)
|
|
||||||
.setCategory(mDashboardCategory)
|
|
||||||
.setSuggestions(suggestions)
|
|
||||||
.setConditionExpanded(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
mDashboardDataWithTwoConditions = new DashboardData.Builder()
|
|
||||||
.setConditions(twoItemsConditions)
|
|
||||||
.setCategory(mDashboardCategory)
|
|
||||||
.setSuggestions(suggestions)
|
|
||||||
.setConditionExpanded(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
mDashboardDataWithNoItems = new DashboardData.Builder()
|
|
||||||
.setConditions(null)
|
|
||||||
.setCategory(null)
|
|
||||||
.setSuggestions(null)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBuildItemsData_shouldSetstableId() {
|
|
||||||
final List<DashboardData.Item> items = mDashboardDataWithOneConditions.getItemList();
|
|
||||||
|
|
||||||
// suggestion, separator, condition, footer, 1 tile
|
|
||||||
assertThat(items).hasSize(5);
|
|
||||||
|
|
||||||
assertThat(items.get(0).id).isEqualTo(STABLE_ID_SUGGESTION_CONTAINER);
|
|
||||||
assertThat(items.get(1).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_DIVIDER);
|
|
||||||
assertThat(items.get(2).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER);
|
|
||||||
assertThat(items.get(3).id).isEqualTo(STABLE_ID_CONDITION_FOOTER);
|
|
||||||
assertThat(items.get(4).id).isEqualTo(TEST_TILE_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBuildItemsData_containsAllData() {
|
|
||||||
final Object[] expectedObjects = {
|
|
||||||
mDashboardDataWithOneConditions.getSuggestions(),
|
|
||||||
null /* divider */,
|
|
||||||
mDashboardDataWithOneConditions.getConditions(),
|
|
||||||
null /* footer */, mTestCategoryTile};
|
|
||||||
final int expectedSize = expectedObjects.length;
|
|
||||||
|
|
||||||
assertThat(mDashboardDataWithOneConditions.getItemList()).hasSize(expectedSize);
|
|
||||||
|
|
||||||
for (int i = 0; i < expectedSize; i++) {
|
|
||||||
final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
|
|
||||||
if (item instanceof List) {
|
|
||||||
assertThat(item).isEqualTo(expectedObjects[i]);
|
|
||||||
} else if (item instanceof DashboardData.ConditionHeaderData) {
|
|
||||||
DashboardData.ConditionHeaderData i1 = (DashboardData.ConditionHeaderData) item;
|
|
||||||
DashboardData.ConditionHeaderData i2 =
|
|
||||||
(DashboardData.ConditionHeaderData) expectedObjects[i];
|
|
||||||
assertThat(i1.title).isEqualTo(i2.title);
|
|
||||||
assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
|
|
||||||
} else {
|
|
||||||
assertThat(item).isSameAs(expectedObjects[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetPositionByEntity_selfInstance_returnPositionFound() {
|
|
||||||
final int position = mDashboardDataWithOneConditions
|
|
||||||
.getPositionByEntity(mDashboardDataWithOneConditions.getConditions());
|
|
||||||
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetPositionByEntity_notExisted_returnNotFound() {
|
|
||||||
final ConditionalCard condition = mock(AirplaneModeConditionCard.class);
|
|
||||||
final int position = mDashboardDataWithOneConditions.getPositionByEntity(condition);
|
|
||||||
assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetPositionByTile_selfInstance_returnPositionFound() {
|
|
||||||
final int position = mDashboardDataWithOneConditions.getPositionByTile(mTestCategoryTile);
|
|
||||||
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetPositionByTile_equalTitle_returnPositionFound() {
|
|
||||||
final Tile tile = mock(Tile.class);
|
|
||||||
when(tile.getId()).thenReturn(TEST_TILE_ID);
|
|
||||||
|
|
||||||
final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
|
|
||||||
|
|
||||||
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetPositionByTile_notExisted_returnNotFound() {
|
|
||||||
final Tile tile = mock(Tile.class);
|
|
||||||
when(tile.getId()).thenReturn(123);
|
|
||||||
final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
|
|
||||||
assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDiffUtil_DataEqual_noResultData() {
|
|
||||||
List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
|
||||||
testDiffUtil(mDashboardDataWithOneConditions,
|
|
||||||
mDashboardDataWithOneConditions, testResultData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDiffUtil_InsertOneCondition_ResultDataOneChanged() {
|
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
|
||||||
// Item in position 3 is the condition container containing the list of conditions, which
|
|
||||||
// gets 1 more item
|
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
|
|
||||||
|
|
||||||
testDiffUtil(mDashboardDataWithOneConditions,
|
|
||||||
mDashboardDataWithTwoConditions, testResultData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDiffUtil_RemoveOneSuggestion_causeItemRemoveAndChange() {
|
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
|
||||||
// removed suggestion and the divider
|
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 2));
|
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
|
|
||||||
// Build DashboardData
|
|
||||||
final List<ConditionalCard> oneItemConditions = new ArrayList<>();
|
|
||||||
|
|
||||||
oneItemConditions.add(mTestCondition);
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
suggestions.add(mTestSuggestion);
|
|
||||||
|
|
||||||
final DashboardData oldData = new DashboardData.Builder()
|
|
||||||
.setConditions(oneItemConditions)
|
|
||||||
.setCategory(mDashboardCategory)
|
|
||||||
.setSuggestions(suggestions)
|
|
||||||
.setConditionExpanded(false)
|
|
||||||
.build();
|
|
||||||
final DashboardData newData = new DashboardData.Builder()
|
|
||||||
.setConditions(oneItemConditions)
|
|
||||||
.setSuggestions(null)
|
|
||||||
.setCategory(mDashboardCategory)
|
|
||||||
.setConditionExpanded(false)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
testDiffUtil(oldData, newData, testResultData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDiffUtil_DeleteAllData_ResultDataOneDeleted() {
|
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 5));
|
|
||||||
|
|
||||||
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDiffUtil_typeSuggestedContainer_ResultDataNothingChanged() {
|
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
|
||||||
|
|
||||||
DashboardData prevData = new DashboardData.Builder()
|
|
||||||
.setConditions(null)
|
|
||||||
.setCategory(null)
|
|
||||||
.setSuggestions(Collections.singletonList(mTestSuggestion))
|
|
||||||
.build();
|
|
||||||
DashboardData currentData = new DashboardData.Builder()
|
|
||||||
.setConditions(null)
|
|
||||||
.setCategory(null)
|
|
||||||
.setSuggestions(Collections.singletonList(mTestSuggestion))
|
|
||||||
.build();
|
|
||||||
testDiffUtil(prevData, currentData, testResultData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test when using the
|
|
||||||
* {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
|
|
||||||
* to transfer List from {@paramref baseDashboardData} to {@paramref diffDashboardData},
|
|
||||||
* whether
|
|
||||||
* the transform data result is equals to {@paramref testResultData}
|
|
||||||
* <p>
|
|
||||||
* The steps are described below:
|
|
||||||
* 1. Calculate a {@link androidx.recyclerview.widget.DiffUtil.DiffResult} from
|
|
||||||
* {@paramref baseDashboardData} to {@paramref diffDashboardData}
|
|
||||||
* <p>
|
|
||||||
* 2. Dispatch the {@link androidx.recyclerview.widget.DiffUtil.DiffResult} calculated from step
|
|
||||||
* 1
|
|
||||||
* into {@link ListUpdateResult}
|
|
||||||
* <p>
|
|
||||||
* 3. Get result data(a.k.a. baseResultData) from {@link ListUpdateResult} and compare it to
|
|
||||||
* {@paramref testResultData}
|
|
||||||
* <p>
|
|
||||||
* Because baseResultData and {@paramref testResultData} don't have sequence. When do the
|
|
||||||
* comparison, we will sort them first and then compare the inside data from them one by one.
|
|
||||||
*/
|
|
||||||
private void testDiffUtil(DashboardData baseDashboardData, DashboardData diffDashboardData,
|
|
||||||
List<ListUpdateResult.ResultData> testResultData) {
|
|
||||||
final DiffUtil.DiffResult diffUtilResult = DiffUtil.calculateDiff(
|
|
||||||
new DashboardData.ItemsDataDiffCallback(
|
|
||||||
baseDashboardData.getItemList(), diffDashboardData.getItemList()));
|
|
||||||
|
|
||||||
// Dispatch to listUpdateResult, then listUpdateResult will have result data
|
|
||||||
final ListUpdateResult listUpdateResult = new ListUpdateResult();
|
|
||||||
diffUtilResult.dispatchUpdatesTo(listUpdateResult);
|
|
||||||
|
|
||||||
final List<ListUpdateResult.ResultData> baseResultData = listUpdateResult.getResultData();
|
|
||||||
assertThat(testResultData.size()).isEqualTo(baseResultData.size());
|
|
||||||
|
|
||||||
// Sort them so we can compare them one by one using a for loop
|
|
||||||
Collections.sort(baseResultData);
|
|
||||||
Collections.sort(testResultData);
|
|
||||||
final int size = baseResultData.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
// Refer to equals method in ResultData
|
|
||||||
assertThat(baseResultData.get(i)).isEqualTo(testResultData.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class contains the result about how the changes made to convert one
|
|
||||||
* list to another list. It implements ListUpdateCallback to record the result data.
|
|
||||||
*/
|
|
||||||
private static class ListUpdateResult implements ListUpdateCallback {
|
|
||||||
final private List<ResultData> mResultData;
|
|
||||||
|
|
||||||
public ListUpdateResult() {
|
|
||||||
mResultData = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ResultData> getResultData() {
|
|
||||||
return mResultData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInserted(int position, int count) {
|
|
||||||
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_INSERT, position, count));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRemoved(int position, int count) {
|
|
||||||
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_REMOVE, position, count));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMoved(int fromPosition, int toPosition) {
|
|
||||||
mResultData.add(
|
|
||||||
new ResultData(ResultData.TYPE_OPERATION_MOVE, fromPosition, toPosition));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChanged(int position, int count, Object payload) {
|
|
||||||
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_CHANGE, position, count));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class contains general type and field to record the operation data generated
|
|
||||||
* in {@link ListUpdateCallback}. Please refer to {@link ListUpdateCallback} for more info.
|
|
||||||
* <p>
|
|
||||||
* The following are examples about the data stored in this class:
|
|
||||||
* <p>
|
|
||||||
* "The data starts from position(arg1) with count number(arg2) is changed(operation)"
|
|
||||||
* or "The data is moved(operation) from position1(arg1) to position2(arg2)"
|
|
||||||
*/
|
|
||||||
private static class ResultData implements Comparable<ResultData> {
|
|
||||||
|
|
||||||
private static final int TYPE_OPERATION_INSERT = 0;
|
|
||||||
private static final int TYPE_OPERATION_REMOVE = 1;
|
|
||||||
private static final int TYPE_OPERATION_MOVE = 2;
|
|
||||||
private static final int TYPE_OPERATION_CHANGE = 3;
|
|
||||||
|
|
||||||
private final int operation;
|
|
||||||
private final int arg1;
|
|
||||||
private final int arg2;
|
|
||||||
|
|
||||||
private ResultData(int operation, int arg1, int arg2) {
|
|
||||||
this.operation = operation;
|
|
||||||
this.arg1 = arg1;
|
|
||||||
this.arg2 = arg2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(obj instanceof ResultData)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultData targetData = (ResultData) obj;
|
|
||||||
|
|
||||||
return operation == targetData.operation && arg1 == targetData.arg1
|
|
||||||
&& arg2 == targetData.arg2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(@NonNull ResultData resultData) {
|
|
||||||
if (this.operation != resultData.operation) {
|
|
||||||
return operation - resultData.operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg1 != resultData.arg1) {
|
|
||||||
return arg1 - resultData.arg1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arg2 - resultData.arg2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "op:" + operation + ",arg1:" + arg1 + ",arg2:" + arg2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.dashboard;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settingslib.drawer.CategoryKey;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
public class DashboardItemAnimatorTest {
|
|
||||||
|
|
||||||
private DashboardItemAnimator mDashboardItemAnimator;
|
|
||||||
private ViewHolder mViewHolder;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void SetUp() {
|
|
||||||
mDashboardItemAnimator = new DashboardItemAnimator();
|
|
||||||
mViewHolder = new ViewHolder(new TextView(RuntimeEnvironment.application));
|
|
||||||
final ActivityInfo activityInfo = new ActivityInfo();
|
|
||||||
activityInfo.packageName = "pkg";
|
|
||||||
activityInfo.name = "class";
|
|
||||||
mViewHolder.itemView.setTag(new Tile(activityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAnimateChange_NoPositionChange_NoPendingAnimation() {
|
|
||||||
final boolean hasPendingAnimation =
|
|
||||||
mDashboardItemAnimator.animateChange(mViewHolder, mViewHolder, 0, 1, 0, 1);
|
|
||||||
assertThat(hasPendingAnimation).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAnimateChange_HasPositionChange_HasPendingAnimation() {
|
|
||||||
final boolean hasPendingAnimation =
|
|
||||||
mDashboardItemAnimator.animateChange(mViewHolder, mViewHolder, 0, 0, 1, 1);
|
|
||||||
assertThat(hasPendingAnimation).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAnimateChange_HasRunningAnimationWhileNoPositionChange_NoPendingAnimation() {
|
|
||||||
// Set pending move animations
|
|
||||||
mDashboardItemAnimator.animateMove(mViewHolder, 0, 0, 1, 1);
|
|
||||||
|
|
||||||
final boolean hasPendingAnimation =
|
|
||||||
mDashboardItemAnimator.animateChange(mViewHolder, mViewHolder, 0, 1, 0, 1);
|
|
||||||
assertThat(hasPendingAnimation).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sample viewholder to use for test
|
|
||||||
static final class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
|
|
||||||
ViewHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 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.dashboard;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
|
||||||
import static org.mockito.Mockito.any;
|
|
||||||
import static org.mockito.Mockito.doNothing;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
|
|
||||||
import com.android.settings.homepage.conditional.ConditionManager;
|
|
||||||
import com.android.settings.homepage.conditional.FocusRecyclerView;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settingslib.drawer.CategoryKey;
|
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
public class DashboardSummaryTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private DashboardAdapter mAdapter;
|
|
||||||
@Mock
|
|
||||||
private DashboardFeatureProvider mDashboardFeatureProvider;
|
|
||||||
@Mock
|
|
||||||
private FocusRecyclerView mDashboard;
|
|
||||||
@Mock
|
|
||||||
private LinearLayoutManager mLayoutManager;
|
|
||||||
@Mock
|
|
||||||
private ConditionManager mConditionManager;
|
|
||||||
@Mock
|
|
||||||
private SummaryLoader mSummaryLoader;
|
|
||||||
@Mock
|
|
||||||
private SuggestionControllerMixinCompat mSuggestionControllerMixin;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private DashboardSummary mSummary;
|
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
mContext = RuntimeEnvironment.application;
|
|
||||||
mSummary = spy(new DashboardSummary());
|
|
||||||
ReflectionHelpers.setField(mSummary, "mAdapter", mAdapter);
|
|
||||||
ReflectionHelpers.setField(mSummary, "mDashboardFeatureProvider",
|
|
||||||
mDashboardFeatureProvider);
|
|
||||||
ReflectionHelpers.setField(mSummary, "mDashboard", mDashboard);
|
|
||||||
ReflectionHelpers.setField(mSummary, "mLayoutManager", mLayoutManager);
|
|
||||||
ReflectionHelpers.setField(mSummary, "mConditionManager", mConditionManager);
|
|
||||||
ReflectionHelpers.setField(mSummary, "mSummaryLoader", mSummaryLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onAttach_suggestionDisabled_shouldNotStartSuggestionControllerMixin() {
|
|
||||||
when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
|
|
||||||
.thenReturn(false);
|
|
||||||
|
|
||||||
mSummary.onAttach(mContext);
|
|
||||||
final SuggestionControllerMixinCompat mixin = ReflectionHelpers
|
|
||||||
.getField(mSummary, "mSuggestionControllerMixin");
|
|
||||||
assertThat(mixin).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onAttach_suggestionEnabled_shouldStartSuggestionControllerMixin() {
|
|
||||||
when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
|
|
||||||
.thenReturn(true);
|
|
||||||
|
|
||||||
mSummary.onAttach(mContext);
|
|
||||||
final SuggestionControllerMixinCompat mixin = ReflectionHelpers
|
|
||||||
.getField(mSummary, "mSuggestionControllerMixin");
|
|
||||||
assertThat(mixin).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateCategory_shouldGetCategoryFromFeatureProvider() {
|
|
||||||
ReflectionHelpers.setField(mSummary, "mSuggestionControllerMixin",
|
|
||||||
mSuggestionControllerMixin);
|
|
||||||
|
|
||||||
when(mSuggestionControllerMixin.isSuggestionLoaded()).thenReturn(true);
|
|
||||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
|
||||||
mSummary.onAttach(mContext);
|
|
||||||
mSummary.updateCategory();
|
|
||||||
|
|
||||||
verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
|
|
||||||
verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
|
|
||||||
verify(mAdapter).setCategory(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateCategory_shouldGetCategoryFromFeatureProvider_evenIfSuggestionDisabled() {
|
|
||||||
when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
|
|
||||||
.thenReturn(false);
|
|
||||||
|
|
||||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
|
||||||
mSummary.onAttach(mContext);
|
|
||||||
mSummary.updateCategory();
|
|
||||||
|
|
||||||
verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
|
|
||||||
verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
|
|
||||||
verify(mAdapter).setCategory(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onConditionChanged_PositionAtTop_ScrollToTop() {
|
|
||||||
when(mLayoutManager.findFirstCompletelyVisibleItemPosition()).thenReturn(1);
|
|
||||||
mSummary.onConditionsChanged();
|
|
||||||
mSummary.onConditionsChanged();
|
|
||||||
verify(mDashboard).scrollToPosition(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onConditionChanged_PositionNotTop_RemainPosition() {
|
|
||||||
when(mLayoutManager.findFirstCompletelyVisibleItemPosition()).thenReturn(2);
|
|
||||||
mSummary.onConditionsChanged();
|
|
||||||
mSummary.onConditionsChanged();
|
|
||||||
verify(mDashboard, never()).scrollToPosition(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onConditionChanged_firstCall_shouldIgnore() {
|
|
||||||
mSummary.onConditionsChanged();
|
|
||||||
verify(mAdapter, never()).setConditions(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onConditionChanged_secondCall_shouldSetConditionsOnAdapter() {
|
|
||||||
mSummary.onConditionsChanged();
|
|
||||||
mSummary.onConditionsChanged();
|
|
||||||
verify(mAdapter).setConditions(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onCategoryChanged_noRebuildOnFirstCall() {
|
|
||||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
|
||||||
doNothing().when(mSummary).rebuildUI();
|
|
||||||
mSummary.onCategoriesChanged();
|
|
||||||
verify(mSummary, never()).rebuildUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onCategoryChanged_rebuildOnSecondCall() {
|
|
||||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
|
||||||
doNothing().when(mSummary).rebuildUI();
|
|
||||||
mSummary.onCategoriesChanged();
|
|
||||||
mSummary.onCategoriesChanged();
|
|
||||||
verify(mSummary).rebuildUI();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,345 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 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.dashboard.suggestions;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.ColorStateList;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapter;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settings.testutils.shadow.ShadowCardView;
|
|
||||||
import com.android.settingslib.Utils;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
|
|
||||||
import com.android.settingslib.utils.IconCache;
|
|
||||||
|
|
||||||
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.RuntimeEnvironment;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
@Config(shadows = ShadowCardView.class)
|
|
||||||
public class SuggestionAdapterTest {
|
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
|
||||||
private SettingsActivity mActivity;
|
|
||||||
@Mock
|
|
||||||
private SuggestionControllerMixinCompat mSuggestionControllerMixin;
|
|
||||||
@Mock
|
|
||||||
private Resources mResources;
|
|
||||||
@Mock
|
|
||||||
private WindowManager mWindowManager;
|
|
||||||
|
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
|
||||||
private Context mContext;
|
|
||||||
private SuggestionAdapter mSuggestionAdapter;
|
|
||||||
private DashboardAdapter.DashboardItemHolder mSuggestionHolder;
|
|
||||||
private List<Suggestion> mOneSuggestion;
|
|
||||||
private List<Suggestion> mTwoSuggestions;
|
|
||||||
private SuggestionAdapter.CardConfig mConfig;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
|
||||||
when(mActivity.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mWindowManager);
|
|
||||||
when(mActivity.getResources()).thenReturn(mResources);
|
|
||||||
when(mResources.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin))
|
|
||||||
.thenReturn(10);
|
|
||||||
when(mResources.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin))
|
|
||||||
.thenReturn(20);
|
|
||||||
mConfig = spy(SuggestionAdapter.CardConfig.get(mActivity));
|
|
||||||
|
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
|
|
||||||
final Suggestion suggestion1 = new Suggestion.Builder("id1")
|
|
||||||
.setTitle("Test suggestion 1")
|
|
||||||
.build();
|
|
||||||
final Suggestion suggestion2 = new Suggestion.Builder("id2")
|
|
||||||
.setTitle("Test suggestion 2")
|
|
||||||
.build();
|
|
||||||
mOneSuggestion = new ArrayList<>();
|
|
||||||
mOneSuggestion.add(suggestion1);
|
|
||||||
mTwoSuggestions = new ArrayList<>();
|
|
||||||
mTwoSuggestions.add(suggestion1);
|
|
||||||
mTwoSuggestions.add(suggestion2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemCount_shouldReturnListSize() {
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
|
||||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
|
|
||||||
|
|
||||||
mSuggestionAdapter.setSuggestions(mTwoSuggestions);
|
|
||||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemViewType_shouldReturnSuggestionTile() {
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
|
||||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
|
||||||
.isEqualTo(R.layout.suggestion_tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemType_hasButton_shouldReturnSuggestionWithButton() {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
suggestions.add(new Suggestion.Builder("id")
|
|
||||||
.setFlags(Suggestion.FLAG_HAS_BUTTON)
|
|
||||||
.setTitle("123")
|
|
||||||
.setSummary("456")
|
|
||||||
.build());
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
|
||||||
.isEqualTo(R.layout.suggestion_tile_with_button);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_shouldLog() {
|
|
||||||
final View view = spy(LayoutInflater.from(mContext).inflate(
|
|
||||||
R.layout.suggestion_tile, new LinearLayout(mContext), true));
|
|
||||||
mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
|
||||||
doReturn("sans").when(mContext).getString(anyInt());
|
|
||||||
|
|
||||||
// Bind twice
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
// Log once
|
|
||||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
|
||||||
mContext, MetricsProto.MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
|
|
||||||
mOneSuggestion.get(0).getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_itemViewShouldHandleClick()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
setupSuggestions(mActivity, suggestions);
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionHolder.itemView.performClick();
|
|
||||||
|
|
||||||
verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
|
|
||||||
verify(suggestions.get(0).getPendingIntent()).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_hasButton_buttonShouldHandleClick()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
final PendingIntent pendingIntent = mock(PendingIntent.class);
|
|
||||||
suggestions.add(new Suggestion.Builder("id")
|
|
||||||
.setFlags(Suggestion.FLAG_HAS_BUTTON)
|
|
||||||
.setTitle("123")
|
|
||||||
.setSummary("456")
|
|
||||||
.setPendingIntent(pendingIntent)
|
|
||||||
.build());
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
doReturn("sans").when(mContext).getString(anyInt());
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionHolder.itemView.findViewById(android.R.id.primary).performClick();
|
|
||||||
|
|
||||||
verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
|
|
||||||
verify(pendingIntent).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getSuggestions_shouldReturnSuggestionWhenMatch() {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
setupSuggestions(mActivity, suggestions);
|
|
||||||
|
|
||||||
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_closeButtonShouldHandleClick()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
final SuggestionAdapter.Callback callback = mock(SuggestionAdapter.Callback.class);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, callback, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionHolder.itemView.findViewById(R.id.close_button).performClick();
|
|
||||||
|
|
||||||
final Suggestion suggestion = suggestions.get(0);
|
|
||||||
verify(mFeatureFactory.suggestionsFeatureProvider).dismissSuggestion(
|
|
||||||
mActivity, mSuggestionControllerMixin, suggestion);
|
|
||||||
verify(callback).onSuggestionClosed(suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_iconNotTintable_shouldNotTintIcon()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final Icon icon = mock(Icon.class);
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder("pkg1")
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.setIcon(icon)
|
|
||||||
.build();
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
IconCache cache = mock(IconCache.class);
|
|
||||||
final Drawable drawable = mock(Drawable.class);
|
|
||||||
when(cache.getIcon(icon)).thenReturn(drawable);
|
|
||||||
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
verify(drawable, never()).setTint(anyInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_iconTintable_shouldTintIcon()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final Icon icon = mock(Icon.class);
|
|
||||||
final int FLAG_ICON_TINTABLE = 1 << 1;
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder("pkg1")
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.setIcon(icon)
|
|
||||||
.setFlags(FLAG_ICON_TINTABLE)
|
|
||||||
.build();
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
IconCache cache = mock(IconCache.class);
|
|
||||||
final Drawable drawable = mock(Drawable.class);
|
|
||||||
when(cache.getIcon(icon)).thenReturn(drawable);
|
|
||||||
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
|
||||||
TypedArray typedArray = mock(TypedArray.class);
|
|
||||||
final ColorStateList colorAccentState = Utils.getColorAccent(mContext);
|
|
||||||
when(mActivity.obtainStyledAttributes(any())).thenReturn(typedArray);
|
|
||||||
when(typedArray.getColorStateList(anyInt())).thenReturn(colorAccentState);
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
verify(drawable).setTintList(colorAccentState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_closeButtonShouldHaveContentDescription()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
setupSuggestions(mActivity, suggestions);
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
assertThat(
|
|
||||||
mSuggestionHolder.itemView.findViewById(R.id.close_button).getContentDescription())
|
|
||||||
.isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setCardLayout_twoCards_shouldSetCardWidthToHalfScreenMinusPadding() {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
setupSuggestions(mContext, suggestions);
|
|
||||||
doReturn(200).when(mConfig).getScreenWidth();
|
|
||||||
|
|
||||||
mConfig.setCardLayout(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* card width = (screen width - left margin - inner margin - right margin) / 2
|
|
||||||
* = (200 - 20 - 10 - 20) / 2
|
|
||||||
* = 75
|
|
||||||
*/
|
|
||||||
assertThat(mSuggestionHolder.itemView.getLayoutParams().width).isEqualTo(75);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSuggestions(Context context, List<Suggestion> suggestions) {
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(context, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Suggestion> makeSuggestions(String... pkgNames) {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
for (String pkgName : pkgNames) {
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder(pkgName)
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.build();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
}
|
|
||||||
return suggestions;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 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.homepage.conditional;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapter;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
public class ConditionAdapterTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ConditionalCard mCondition1;
|
|
||||||
@Mock
|
|
||||||
private ConditionalCard mCondition2;
|
|
||||||
@Mock
|
|
||||||
private ConditionManager mConditionManager;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private ConditionAdapter mConditionAdapter;
|
|
||||||
private List<ConditionalCard> mOneCondition;
|
|
||||||
private List<ConditionalCard> mTwoConditions;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mContext = RuntimeEnvironment.application;
|
|
||||||
final CharSequence action = "action";
|
|
||||||
when(mCondition1.getActionText()).thenReturn(action);
|
|
||||||
mOneCondition = new ArrayList<>();
|
|
||||||
mOneCondition.add(mCondition1);
|
|
||||||
mTwoConditions = new ArrayList<>();
|
|
||||||
mTwoConditions.add(mCondition1);
|
|
||||||
mTwoConditions.add(mCondition2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemCount_notExpanded_shouldReturn0() {
|
|
||||||
mConditionAdapter = new ConditionAdapter(mContext, mConditionManager, mOneCondition, false);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemCount_expanded_shouldReturnListSize() {
|
|
||||||
mConditionAdapter = new ConditionAdapter(mContext, mConditionManager, mOneCondition, true);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(1);
|
|
||||||
|
|
||||||
mConditionAdapter = new ConditionAdapter(mContext, mConditionManager, mTwoConditions, true);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemViewType_shouldReturnConditionTile() {
|
|
||||||
mConditionAdapter = new ConditionAdapter(mContext, mConditionManager, mTwoConditions, true);
|
|
||||||
assertThat(mConditionAdapter.getItemViewType(0)).isEqualTo(R.layout.condition_tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_shouldSetListener() {
|
|
||||||
final View view = LayoutInflater.from(mContext)
|
|
||||||
.inflate(R.layout.condition_tile, new LinearLayout(mContext), true);
|
|
||||||
final DashboardAdapter.DashboardItemHolder viewHolder =
|
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
|
||||||
mConditionAdapter = new ConditionAdapter(mContext, mConditionManager, mOneCondition, true);
|
|
||||||
|
|
||||||
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
|
||||||
final View card = view.findViewById(R.id.content);
|
|
||||||
assertThat(card).isNotNull();
|
|
||||||
assertThat(card.hasOnClickListeners()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void viewClick_shouldInvokeConditionPrimaryClick() {
|
|
||||||
final View view = LayoutInflater.from(mContext)
|
|
||||||
.inflate(R.layout.condition_tile, new LinearLayout(mContext), true);
|
|
||||||
final DashboardAdapter.DashboardItemHolder viewHolder =
|
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
|
||||||
mConditionAdapter = new ConditionAdapter(mContext, mConditionManager, mOneCondition, true);
|
|
||||||
|
|
||||||
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
|
||||||
final View card = view.findViewById(R.id.content);
|
|
||||||
assertThat(card).isNotNull();
|
|
||||||
card.performClick();
|
|
||||||
verify(mConditionManager).onPrimaryClick(any(Context.class), anyLong());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -17,17 +17,10 @@ package com.android.settings.network;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
|
|
||||||
import com.android.settings.dashboard.SummaryLoader;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
|
|
||||||
import com.android.settingslib.drawer.CategoryKey;
|
import com.android.settingslib.drawer.CategoryKey;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -65,90 +58,4 @@ public class NetworkDashboardFragmentTest {
|
|||||||
assertThat(indexRes).hasSize(1);
|
assertThat(indexRes).hasSize(1);
|
||||||
assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
|
assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void summaryProviderSetListening_hasMobileAndHotspot_shouldReturnMobileSummary() {
|
|
||||||
final WifiMasterSwitchPreferenceController wifiPreferenceController =
|
|
||||||
mock(WifiMasterSwitchPreferenceController.class);
|
|
||||||
final MobileNetworkPreferenceController mobileNetworkPreferenceController =
|
|
||||||
mock(MobileNetworkPreferenceController.class);
|
|
||||||
final TetherPreferenceController tetherPreferenceController =
|
|
||||||
mock(TetherPreferenceController.class);
|
|
||||||
|
|
||||||
final SummaryLoader summaryLoader = mock(SummaryLoader.class);
|
|
||||||
final SummaryLoader.SummaryProvider provider =
|
|
||||||
new NetworkDashboardFragment.SummaryProvider(mContext, summaryLoader,
|
|
||||||
wifiPreferenceController, mobileNetworkPreferenceController,
|
|
||||||
tetherPreferenceController, () -> true);
|
|
||||||
|
|
||||||
provider.setListening(false);
|
|
||||||
|
|
||||||
verifyZeroInteractions(summaryLoader);
|
|
||||||
|
|
||||||
when(wifiPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
when(mobileNetworkPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
when(tetherPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
|
|
||||||
provider.setListening(true);
|
|
||||||
|
|
||||||
verify(summaryLoader).setSummary(provider, "Wi\u2011Fi, mobile, data usage, and hotspot");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void summaryProviderSetListening_noMobileOrHotspot_shouldReturnSimpleSummary() {
|
|
||||||
final WifiMasterSwitchPreferenceController wifiPreferenceController =
|
|
||||||
mock(WifiMasterSwitchPreferenceController.class);
|
|
||||||
final MobileNetworkPreferenceController mobileNetworkPreferenceController =
|
|
||||||
mock(MobileNetworkPreferenceController.class);
|
|
||||||
final TetherPreferenceController tetherPreferenceController =
|
|
||||||
mock(TetherPreferenceController.class);
|
|
||||||
|
|
||||||
final SummaryLoader summaryLoader = mock(SummaryLoader.class);
|
|
||||||
final SummaryLoader.SummaryProvider provider =
|
|
||||||
new NetworkDashboardFragment.SummaryProvider(mContext, summaryLoader,
|
|
||||||
wifiPreferenceController, mobileNetworkPreferenceController,
|
|
||||||
tetherPreferenceController, () -> true);
|
|
||||||
|
|
||||||
provider.setListening(false);
|
|
||||||
|
|
||||||
verifyZeroInteractions(summaryLoader);
|
|
||||||
|
|
||||||
when(wifiPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
when(mobileNetworkPreferenceController.isAvailable()).thenReturn(false);
|
|
||||||
when(tetherPreferenceController.isAvailable()).thenReturn(false);
|
|
||||||
|
|
||||||
provider.setListening(true);
|
|
||||||
|
|
||||||
verify(summaryLoader).setSummary(provider, "Wi\u2011Fi and data usage");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void summaryProviderSetListening_noDataUsageActivity_shouldReturnNoDataUsageSummary() {
|
|
||||||
final WifiMasterSwitchPreferenceController wifiPreferenceController =
|
|
||||||
mock(WifiMasterSwitchPreferenceController.class);
|
|
||||||
final MobileNetworkPreferenceController mobileNetworkPreferenceController =
|
|
||||||
mock(MobileNetworkPreferenceController.class);
|
|
||||||
final TetherPreferenceController tetherPreferenceController =
|
|
||||||
mock(TetherPreferenceController.class);
|
|
||||||
|
|
||||||
final SummaryLoader summaryLoader = mock(SummaryLoader.class);
|
|
||||||
final SummaryLoader.SummaryProvider provider =
|
|
||||||
new NetworkDashboardFragment.SummaryProvider(mContext, summaryLoader,
|
|
||||||
wifiPreferenceController, mobileNetworkPreferenceController,
|
|
||||||
tetherPreferenceController, () -> false);
|
|
||||||
|
|
||||||
provider.setListening(false);
|
|
||||||
|
|
||||||
verifyZeroInteractions(summaryLoader);
|
|
||||||
|
|
||||||
when(wifiPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
when(mobileNetworkPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
when(tetherPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
|
|
||||||
provider.setListening(true);
|
|
||||||
|
|
||||||
verify(summaryLoader).setSummary(provider, "Wi\u2011Fi, mobile, and hotspot");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.dashboard;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import android.app.Instrumentation;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.support.test.InstrumentationRegistry;
|
|
||||||
import android.support.test.filters.SmallTest;
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.support.test.uiautomator.By;
|
|
||||||
import android.support.test.uiautomator.UiDevice;
|
|
||||||
import android.support.test.uiautomator.UiObject2;
|
|
||||||
import android.support.test.uiautomator.Until;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@SmallTest
|
|
||||||
public class DashboardSummaryInstrumentationTest {
|
|
||||||
|
|
||||||
private static final long TIMEOUT = 2000l;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private Instrumentation mInstrumentation;
|
|
||||||
|
|
||||||
private UiDevice mDevice;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
mInstrumentation = InstrumentationRegistry.getInstrumentation();
|
|
||||||
mDevice = UiDevice.getInstance(mInstrumentation);
|
|
||||||
mContext = InstrumentationRegistry.getTargetContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void rotate_shouldSaveCategoriesChangedState() {
|
|
||||||
final Intent intent = new Intent(Settings.ACTION_SETTINGS)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
final FragmentActivity activity =
|
|
||||||
(FragmentActivity) mInstrumentation.startActivitySync(intent);
|
|
||||||
|
|
||||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
|
||||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
||||||
|
|
||||||
final UiObject2 item = mDevice.wait(Until.findObject(By.res("android:id/title")
|
|
||||||
.text("Network & internet")), TIMEOUT);
|
|
||||||
assertThat(item).isNotNull();
|
|
||||||
|
|
||||||
final List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
|
|
||||||
final DashboardSummary fragment = (DashboardSummary) fragments.get(0);
|
|
||||||
|
|
||||||
assertThat(fragment.mIsOnCategoriesChangedCalled).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Reference in New Issue
Block a user