From 78369d91bcdc34a01269cf33fc231aee76c1b593 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 12 Oct 2017 11:09:24 -0700 Subject: [PATCH] Switch to recyclerview in ManageApplications - Replace ListView with RecyclerView in layout - Replace ApplicationAdapter's superclass to be RecyclerView.Adapter - Change adapter interfaces (where necessary) to work with RecyclerView - Replace fast scroll with Recycler's mechanism (all in xml) - Removed section indexer (text bubble when fast scroll) because recyclerview doesn't support it. Bug: 64804294 Test: robotests Change-Id: I55b221836ce6abdeddf4568c8a8a5632cbddbd3b --- res/drawable/line.xml | 27 ++ res/drawable/line_drawable.xml | 25 + res/drawable/thumb.xml | 32 ++ res/drawable/thumb_drawable.xml | 24 + res/layout/manage_applications_apps.xml | 59 ++- .../ApplicationViewHolder.java | 122 +++++ .../FileViewHolderController.java | 4 +- .../ManageApplications.java | 459 +++++------------- .../MusicViewHolderController.java | 8 +- .../PhotosViewHolderController.java | 8 +- .../settings/development/AppPicker.java | 1 - .../AppViewHolder.java | 23 +- .../android/settings/display/AppGridView.java | 4 +- .../ApplicationViewHolderTest.java | 95 ++++ .../ManageApplicationsTest.java | 54 +-- .../MusicViewHolderControllerTest.java | 19 +- .../PhotosViewHolderControllerTest.java | 18 +- 17 files changed, 534 insertions(+), 448 deletions(-) create mode 100644 res/drawable/line.xml create mode 100644 res/drawable/line_drawable.xml create mode 100644 res/drawable/thumb.xml create mode 100644 res/drawable/thumb_drawable.xml create mode 100644 src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java rename src/com/android/settings/{applications/manageapplications => development}/AppViewHolder.java (71%) create mode 100644 tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java diff --git a/res/drawable/line.xml b/res/drawable/line.xml new file mode 100644 index 00000000000..786ed1c48ef --- /dev/null +++ b/res/drawable/line.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/res/drawable/line_drawable.xml b/res/drawable/line_drawable.xml new file mode 100644 index 00000000000..a524c6145eb --- /dev/null +++ b/res/drawable/line_drawable.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/res/drawable/thumb.xml b/res/drawable/thumb.xml new file mode 100644 index 00000000000..42fbd8cc115 --- /dev/null +++ b/res/drawable/thumb.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/res/drawable/thumb_drawable.xml b/res/drawable/thumb_drawable.xml new file mode 100644 index 00000000000..b99536dec95 --- /dev/null +++ b/res/drawable/thumb_drawable.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/res/layout/manage_applications_apps.xml b/res/layout/manage_applications_apps.xml index e4ec8715896..d36252878c3 100644 --- a/res/layout/manage_applications_apps.xml +++ b/res/layout/manage_applications_apps.xml @@ -14,44 +14,53 @@ limitations under the License. --> - + + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> - + - + - + settings:fastScrollEnabled="true" + settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable" + settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable" + settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" + settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/> - + diff --git a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java new file mode 100644 index 00000000000..9ac2167bfcb --- /dev/null +++ b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java @@ -0,0 +1,122 @@ +/* + * 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.applications.manageapplications; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.support.annotation.StringRes; +import android.support.annotation.VisibleForTesting; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; + +public class ApplicationViewHolder extends RecyclerView.ViewHolder { + + private final TextView mAppName; + private final ImageView mAppIcon; + + @VisibleForTesting + final TextView mSummary; + @VisibleForTesting + final TextView mDisabled; + + ApplicationViewHolder(View itemView) { + super(itemView); + mAppName = itemView.findViewById(android.R.id.title); + mAppIcon = itemView.findViewById(android.R.id.icon); + mSummary = itemView.findViewById(R.id.widget_text1); + mDisabled = itemView.findViewById(R.id.widget_text2); + } + + static View newView(LayoutInflater inflater, ViewGroup parent) { + final View root = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.preference_app, parent, false); + inflater.inflate(R.layout.widget_text_views, + root.findViewById(android.R.id.widget_frame)); + return root; + } + + void setSummary(CharSequence summary) { + mSummary.setText(summary); + } + + void setSummary(@StringRes int summary) { + mSummary.setText(summary); + } + + void setEnabled(boolean isEnabled) { + itemView.setEnabled(isEnabled); + } + + void setTitle(CharSequence title) { + if (title == null) { + return; + } + mAppName.setText(title); + } + + void setIcon(Drawable icon) { + if (icon == null) { + return; + } + mAppIcon.setImageDrawable(icon); + } + + void updateDisableView(ApplicationInfo info) { + if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { + mDisabled.setVisibility(View.VISIBLE); + mDisabled.setText(R.string.not_installed); + } else if (!info.enabled || info.enabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { + mDisabled.setVisibility(View.VISIBLE); + mDisabled.setText(R.string.disabled); + } else { + mDisabled.setVisibility(View.GONE); + } + } + + void updateSizeText(AppEntry entry, CharSequence invalidSizeStr, int whichSize) { + if (ManageApplications.DEBUG) { + Log.d(ManageApplications.TAG, "updateSizeText of " + + entry.label + " " + entry + ": " + entry.sizeStr); + } + if (entry.sizeStr != null) { + switch (whichSize) { + case ManageApplications.SIZE_INTERNAL: + setSummary(entry.internalSizeStr); + break; + case ManageApplications.SIZE_EXTERNAL: + setSummary(entry.externalSizeStr); + break; + default: + setSummary(entry.sizeStr); + break; + } + } else if (entry.size == ApplicationsState.SIZE_INVALID) { + setSummary(invalidSizeStr); + } + } +} diff --git a/src/com/android/settings/applications/manageapplications/FileViewHolderController.java b/src/com/android/settings/applications/manageapplications/FileViewHolderController.java index 03e2e55a87b..4e6bf262530 100644 --- a/src/com/android/settings/applications/manageapplications/FileViewHolderController.java +++ b/src/com/android/settings/applications/manageapplications/FileViewHolderController.java @@ -35,12 +35,14 @@ public interface FileViewHolderController { /** * Initializes the view within an AppViewHolder. + * * @param holder The holder to use to initialize. */ - void setupView(AppViewHolder holder); + void setupView(ApplicationViewHolder holder); /** * Handles the behavior when the view is clicked. + * * @param fragment Fragment where the click originated. */ void onClick(Fragment fragment); diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 932b2dd7c23..6b21128c691 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -40,16 +40,14 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageItemInfo; -import android.content.pm.PackageManager; -import android.icu.text.AlphabeticIndex; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; -import android.os.LocaleList; import android.os.UserHandle; import android.os.UserManager; import android.preference.PreferenceFrameLayout; import android.support.annotation.VisibleForTesting; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; @@ -59,19 +57,11 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AbsListView; import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; -import android.widget.BaseAdapter; -import android.widget.Filter; -import android.widget.Filterable; import android.widget.FrameLayout; -import android.widget.ListView; -import android.widget.SectionIndexer; import android.widget.Spinner; -import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; @@ -86,7 +76,6 @@ import com.android.settings.Settings.StorageUseActivity; import com.android.settings.Settings.UsageAccessSettingsActivity; import com.android.settings.Settings.WriteSettingsActivity; import com.android.settings.SettingsActivity; -import com.android.settings.Utils; import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; import com.android.settings.applications.AppStateBaseBridge; @@ -122,13 +111,13 @@ import com.android.settingslib.applications.ApplicationsState.AppFilter; import com.android.settingslib.applications.ApplicationsState.CompoundFilter; import com.android.settingslib.applications.ApplicationsState.VolumeFilter; import com.android.settingslib.applications.StorageStatsSource; +import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.wrapper.PackageManagerWrapper; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.Locale; import java.util.Set; /** @@ -138,7 +127,7 @@ import java.util.Set; * intent. */ public class ManageApplications extends InstrumentedPreferenceFragment - implements OnItemClickListener, OnItemSelectedListener { + implements View.OnClickListener, OnItemSelectedListener { static final String TAG = "ManageApplications"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -185,23 +174,17 @@ public class ManageApplications extends InstrumentedPreferenceFragment private ApplicationsState mApplicationsState; public int mListType; - public AppFilterItem mFilter; - - public ApplicationsAdapter mApplications; + private AppFilterItem mFilter; + private ApplicationsAdapter mApplications; private View mLoadingContainer; private View mListContainer; - - // ListView used to display list - private ListView mListView; + private RecyclerView mRecyclerView; // Size resource used for packages whose size computation failed for some reason CharSequence mInvalidSizeStr; - // layout inflater object used to inflate views - private LayoutInflater mInflater; - private String mCurrentPkgName; private int mCurrentUid; @@ -234,6 +217,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment private int mStorageType; private boolean mIsWorkOnly; private int mWorkUserId; + private View mEmptyView; @Override public void onCreate(Bundle savedInstanceState) { @@ -304,25 +288,14 @@ public class ManageApplications extends InstrumentedPreferenceFragment @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // initialize the inflater - mInflater = inflater; - mRootView = inflater.inflate(R.layout.manage_applications_apps, null); mLoadingContainer = mRootView.findViewById(R.id.loading_container); mListContainer = mRootView.findViewById(R.id.list_container); if (mListContainer != null) { // Create adapter and list view here - View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty); - ListView lv = mListContainer.findViewById(android.R.id.list); - if (emptyView != null) { - lv.setEmptyView(emptyView); - } - lv.setOnItemClickListener(this); - lv.setSaveEnabled(true); - lv.setItemsCanFocus(true); - lv.setTextFilterEnabled(true); - mListView = lv; - mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter); + mEmptyView = mListContainer.findViewById(android.R.id.empty); + mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter, + savedInstanceState); if (savedInstanceState != null) { mApplications.mHasReceivedLoadEntries = savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false); @@ -347,11 +320,10 @@ public class ManageApplications extends InstrumentedPreferenceFragment mVolumeUuid, UserHandle.of(userId))); } - mListView.setAdapter(mApplications); - mListView.setRecyclerListener(mApplications); - mListView.setFastScrollEnabled(isFastScrollEnabled()); - - Utils.prepareCustomPreferencesList(container, mRootView, mListView, false); + mRecyclerView = mListContainer.findViewById(R.id.apps_list); + mRecyclerView.setLayoutManager(new LinearLayoutManager( + getContext(), RecyclerView.VERTICAL, false /* reverseLayout */)); + mRecyclerView.setAdapter(mApplications); } // We have to do this now because PreferenceFrameLayout looks at it @@ -492,6 +464,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries); outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback); + if (mApplications != null) { + mApplications.onSaveInstanceState(outState); + } } @Override @@ -633,7 +608,6 @@ public class ManageApplications extends InstrumentedPreferenceFragment case R.id.sort_order_alpha: case R.id.sort_order_size: mSortOrder = menuId; - mListView.setFastScrollEnabled(isFastScrollEnabled()); if (mApplications != null) { mApplications.rebuild(mSortOrder); } @@ -667,10 +641,11 @@ public class ManageApplications extends InstrumentedPreferenceFragment } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { + public void onClick(View view) { if (mApplications == null) { return; } + final int position = mRecyclerView.getChildAdapterPosition(view); if (mApplications.getApplicationCount() > position) { ApplicationsState.AppEntry entry = mApplications.getAppEntry(position); @@ -757,7 +732,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE); notifyDataSetChanged(); if (mFilterOptions.size() == 1) { - if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter); + if (DEBUG) { + Log.d(TAG, "Auto selecting filter " + filter); + } mManageApplications.mFilterSpinner.setSelection(0); mManageApplications.onItemSelected(null, null, 0, 0); } @@ -777,7 +754,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment notifyDataSetChanged(); if (mManageApplications.mFilter == filter) { if (mFilterOptions.size() > 0) { - if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0)); + if (DEBUG) { + Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0)); + } mManageApplications.mFilterSpinner.setSelection(0); mManageApplications.onItemSelected(null, null, 0, 0); } @@ -795,38 +774,25 @@ public class ManageApplications extends InstrumentedPreferenceFragment } } - /* - * Custom adapter implementation for the ListView - * This adapter maintains a map for each displayed application and its properties - * An index value on each AppInfo object indicates the correct position or index - * in the list. If the list gets updated dynamically when the user is viewing the list of - * applications, we need to return the correct index of position. This is done by mapping - * the getId methods via the package name into the internal maps and indices. - * The order of applications in the list is mirrored in mAppLocalList - */ - static class ApplicationsAdapter extends BaseAdapter implements Filterable, - ApplicationsState.Callbacks, AppStateBaseBridge.Callback, - AbsListView.RecyclerListener, SectionIndexer { + static class ApplicationsAdapter extends RecyclerView.Adapter + implements ApplicationsState.Callbacks, AppStateBaseBridge.Callback { - private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0]; + private static final String STATE_LAST_SCROLL_INDEX = "state_last_scroll_index"; + private static final int VIEW_TYPE_APP = 0; + private static final int VIEW_TYPE_EXTRA_VIEW = 1; private final ApplicationsState mState; private final ApplicationsState.Session mSession; private final ManageApplications mManageApplications; private final Context mContext; - private final ArrayList mActive = new ArrayList<>(); private final AppStateBaseBridge mExtraInfoBridge; - private final Handler mBgHandler; - private final Handler mFgHandler; private final LoadingViewController mLoadingViewController; private AppFilterItem mAppFilter; - private ArrayList mBaseEntries; private ArrayList mEntries; private boolean mResumed; private int mLastSortMode = -1; private int mWhichSize = SIZE_TOTAL; - CharSequence mCurFilterPrefix; private AppFilter mCompositeFilter; private boolean mHasReceivedLoadEntries; private boolean mHasReceivedBridgeCallback; @@ -836,39 +802,11 @@ public class ManageApplications extends InstrumentedPreferenceFragment // fragment is paused. We need this special handling because app entries are added gradually // when we rebuild the list after the user made some changes, like uninstalling an app. private int mLastIndex = -1; - private int mLastTop; - - private AlphabeticIndex.ImmutableIndex mIndex; - private SectionInfo[] mSections = EMPTY_SECTIONS; - private int[] mPositionToSectionIndex; - - private Filter mFilter = new Filter() { - @Override - protected FilterResults performFiltering(CharSequence constraint) { - ArrayList entries - = applyPrefixFilter(constraint, mBaseEntries); - FilterResults fr = new FilterResults(); - fr.values = entries; - fr.count = entries.size(); - return fr; - } - - @Override - @SuppressWarnings("unchecked") - protected void publishResults(CharSequence constraint, FilterResults results) { - mCurFilterPrefix = constraint; - mEntries = (ArrayList) results.values; - rebuildSections(); - notifyDataSetChanged(); - } - }; - public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications, - AppFilterItem appFilter) { + AppFilterItem appFilter, Bundle savedInstanceState) { + setHasStableIds(true); mState = state; - mFgHandler = new Handler(); - mBgHandler = new Handler(mState.getBackgroundLooper()); mSession = state.newSession(this); mManageApplications = manageApplications; mLoadingViewController = new LoadingViewController( @@ -893,6 +831,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment } else { mExtraInfoBridge = null; } + if (savedInstanceState != null) { + mLastIndex = savedInstanceState.getInt(STATE_LAST_SCROLL_INDEX); + } } public void setCompositeFilter(AppFilter compositeFilter) { @@ -907,9 +848,11 @@ public class ManageApplications extends InstrumentedPreferenceFragment public void setExtraViewController(FileViewHolderController extraViewController) { mExtraViewController = extraViewController; - mBgHandler.post(() -> { + // Start to query extra view's stats on background, and once done post result to main + // thread. + ThreadUtils.postOnBackgroundThread(() -> { mExtraViewController.queryStats(); - mFgHandler.post(() -> { + ThreadUtils.postOnMainThread(() -> { onExtraViewCompleted(); }); }); @@ -938,11 +881,13 @@ public class ManageApplications extends InstrumentedPreferenceFragment mExtraInfoBridge.pause(); } } + } + + public void onSaveInstanceState(Bundle outState) { // Record the current scroll position before pausing. - mLastIndex = mManageApplications.mListView.getFirstVisiblePosition(); - View v = mManageApplications.mListView.getChildAt(0); - mLastTop = - (v == null) ? 0 : (v.getTop() - mManageApplications.mListView.getPaddingTop()); + final LinearLayoutManager layoutManager = + (LinearLayoutManager) mManageApplications.mRecyclerView.getLayoutManager(); + outState.putInt(STATE_LAST_SCROLL_INDEX, layoutManager.findFirstVisibleItemPosition()); } public void release() { @@ -960,6 +905,21 @@ public class ManageApplications extends InstrumentedPreferenceFragment rebuild(); } + @Override + public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + final View view = ApplicationViewHolder.newView( + LayoutInflater.from(parent.getContext()), parent); + return new ApplicationViewHolder(view); + } + + @Override + public int getItemViewType(int position) { + boolean isLastItem = (getItemCount() - 1) == position; + return hasExtraView() && isLastItem + ? VIEW_TYPE_EXTRA_VIEW + : VIEW_TYPE_APP; + } + public void rebuild() { if (!mHasReceivedLoadEntries || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) { @@ -1008,11 +968,11 @@ public class ManageApplications extends InstrumentedPreferenceFragment filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE); AppFilter finalFilterObj = filterObj; - mBgHandler.post(() -> { + ThreadUtils.postOnBackgroundThread(() -> { final ArrayList entries = mSession.rebuild(finalFilterObj, comparatorObj, false); if (entries != null) { - mFgHandler.post(() -> onRebuildComplete(entries)); + ThreadUtils.postOnMainThread(() -> onRebuildComplete(entries)); } }); } @@ -1031,8 +991,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment ArrayList entries) { int size = entries.size(); // returnList will not have more entries than entries - ArrayList returnEntries = new - ArrayList(size); + ArrayList returnEntries = new ArrayList<>(size); // assume appinfo of same package but different users are grouped together PackageItemInfo lastInfo = null; @@ -1055,21 +1014,19 @@ public class ManageApplications extends InstrumentedPreferenceFragment filterType == FILTER_APPS_POWER_WHITELIST_ALL) { entries = removeDuplicateIgnoringUser(entries); } - mBaseEntries = entries; - if (mBaseEntries != null) { - mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); - rebuildSections(); - } else { - mEntries = null; - mSections = EMPTY_SECTIONS; - mPositionToSectionIndex = null; - } - + mEntries = entries; notifyDataSetChanged(); + if (getItemCount() == 0) { + mManageApplications.mRecyclerView.setVisibility(View.GONE); + mManageApplications.mEmptyView.setVisibility(View.VISIBLE); + } else { + mManageApplications.mEmptyView.setVisibility(View.GONE); + mManageApplications.mRecyclerView.setVisibility(View.VISIBLE); + } // Restore the last scroll position if the number of entries added so far is bigger than // it. - if (mLastIndex != -1 && getCount() > mLastIndex) { - mManageApplications.mListView.setSelectionFromTop(mLastIndex, mLastTop); + if (mLastIndex != -1 && getItemCount() > mLastIndex) { + mManageApplications.mRecyclerView.getLayoutManager().scrollToPosition(mLastIndex); mLastIndex = -1; } @@ -1086,45 +1043,6 @@ public class ManageApplications extends InstrumentedPreferenceFragment mManageApplications.setHasInstant(mState.haveInstantApps()); } - private void rebuildSections() { - if (mEntries != null && mManageApplications.mListView.isFastScrollEnabled()) { - // Rebuild sections - if (mIndex == null) { - LocaleList locales = mContext.getResources().getConfiguration().getLocales(); - if (locales.size() == 0) { - locales = new LocaleList(Locale.ENGLISH); - } - AlphabeticIndex index = new AlphabeticIndex(locales.get(0)); - int localeCount = locales.size(); - for (int i = 1; i < localeCount; i++) { - index.addLabels(locales.get(i)); - } - // Ensure we always have some base English locale buckets - index.addLabels(Locale.ENGLISH); - mIndex = index.buildImmutableIndex(); - } - - ArrayList sections = new ArrayList<>(); - int lastSecId = -1; - int totalEntries = mEntries.size(); - mPositionToSectionIndex = new int[totalEntries]; - - for (int pos = 0; pos < totalEntries; pos++) { - String label = mEntries.get(pos).label; - int secId = mIndex.getBucketIndex(TextUtils.isEmpty(label) ? "" : label); - if (secId != lastSecId) { - lastSecId = secId; - sections.add(new SectionInfo(mIndex.getBucket(secId).getLabel(), pos)); - } - mPositionToSectionIndex[pos] = sections.size() - 1; - } - mSections = sections.toArray(EMPTY_SECTIONS); - } else { - mSections = EMPTY_SECTIONS; - mPositionToSectionIndex = null; - } - } - @VisibleForTesting void updateLoading() { final boolean appLoaded = mHasReceivedLoadEntries && mSession.getAllApps().size() != 0; @@ -1135,26 +1053,6 @@ public class ManageApplications extends InstrumentedPreferenceFragment } } - ArrayList applyPrefixFilter(CharSequence prefix, - ArrayList origEntries) { - if (prefix == null || prefix.length() == 0) { - return origEntries; - } else { - String prefixStr = ApplicationsState.normalize(prefix.toString()); - final String spacePrefixStr = " " + prefixStr; - ArrayList newEntries - = new ArrayList(); - for (int i = 0; i < origEntries.size(); i++) { - ApplicationsState.AppEntry entry = origEntries.get(i); - String nlabel = entry.getNormalizedLabel(); - if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) { - newEntries.add(entry); - } - } - return newEntries; - } - } - @Override public void onExtraInfoUpdated() { mHasReceivedBridgeCallback = true; @@ -1186,29 +1084,27 @@ public class ManageApplications extends InstrumentedPreferenceFragment @Override public void onPackageSizeChanged(String packageName) { - for (int i = 0; i < mActive.size(); i++) { - AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag(); - if (holder == null || holder.entry == null) { + if (mEntries == null) { + return; + } + final int size = mEntries.size(); + for (int i = 0; i < size; i++) { + final AppEntry entry = mEntries.get(i); + final ApplicationInfo info = entry.info; + if (info == null && !TextUtils.equals(packageName, info.packageName)) { continue; } - ApplicationInfo info = holder.entry.info; - if (info == null) { - continue; - } - if (holder.entry.info.packageName.equals(packageName)) { - synchronized (holder.entry) { - updateSummary(holder); - } - if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName) - && mLastSortMode == R.id.sort_order_size) { - // We got the size information for the last app the - // user viewed, and are sorting by size... they may - // have cleared data, so we immediately want to resort - // the list with the new size to reflect it to the user. - rebuild(); - } + if (TextUtils.equals(mManageApplications.mCurrentPkgName, info.packageName)) { + // We got the size information for the last app the + // user viewed, and are sorting by size... they may + // have cleared data, so we immediately want to resort + // the list with the new size to reflect it to the user. + rebuild(); return; + } else { + notifyItemChanged(i); } + } } @@ -1227,46 +1123,30 @@ public class ManageApplications extends InstrumentedPreferenceFragment } public void onExtraViewCompleted() { - int size = mActive.size(); - // If we have no elements, don't do anything. - if (size < 1) { + if (!hasExtraView()) { return; } - AppViewHolder holder = (AppViewHolder) mActive.get(size - 1).getTag(); - - // HACK: The extra view has no AppEntry -- and should be the only element without one. - // Thus, if the last active element has no AppEntry, it is the extra view. - if (holder == null || holder.entry != null) { - return; - } - - mExtraViewController.setupView(holder); + // Update last item - this is assumed to be the extra view. + notifyItemChanged(getItemCount() - 1); } - public int getCount() { + @Override + public int getItemCount() { if (mEntries == null) { return 0; } - int extraViewAddition = - (mExtraViewController != null && mExtraViewController.shouldShow()) ? 1 : 0; - return mEntries.size() + extraViewAddition; + return mEntries.size() + (hasExtraView() ? 1 : 0); } public int getApplicationCount() { return mEntries != null ? mEntries.size() : 0; } - public Object getItem(int position) { - if (position == mEntries.size()) { - return mExtraViewController; - } - return mEntries.get(position); - } - - public ApplicationsState.AppEntry getAppEntry(int position) { + public AppEntry getAppEntry(int position) { return mEntries.get(position); } + @Override public long getItemId(int position) { if (position == mEntries.size()) { return -1; @@ -1274,142 +1154,76 @@ public class ManageApplications extends InstrumentedPreferenceFragment return mEntries.get(position).id; } - @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override public boolean isEnabled(int position) { - if (position == mEntries.size() && mExtraViewController != null && - mExtraViewController.shouldShow()) { - return true; - } - - if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) { + if (getItemViewType(position) == VIEW_TYPE_EXTRA_VIEW + || mManageApplications.mListType != LIST_TYPE_HIGH_POWER) { return true; } ApplicationsState.AppEntry entry = mEntries.get(position); return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName); } - public View getView(int position, View convertView, ViewGroup parent) { - // A ViewHolder keeps references to children views to avoid unnecessary calls - // to findViewById() on each row. - AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater, - convertView); - convertView = holder.rootView; - - // Handle the extra view if it is the last entry. + @Override + public void onBindViewHolder(ApplicationViewHolder holder, int position) { if (mEntries != null && mExtraViewController != null && position == mEntries.size()) { + // set up view for extra view controller mExtraViewController.setupView(holder); - convertView.setEnabled(true); } else { // Bind the data efficiently with the holder ApplicationsState.AppEntry entry = mEntries.get(position); synchronized (entry) { - holder.entry = entry; - if (entry.label != null) { - holder.appName.setText(entry.label); - } + holder.setTitle(entry.label); mState.ensureIcon(entry); - if (entry.icon != null) { - holder.appIcon.setImageDrawable(entry.icon); - } - updateSummary(holder); - updateDisableView(holder.disabled, entry.info); + holder.setIcon(entry.icon); + updateSummary(holder, entry); + holder.updateDisableView(entry.info); } - convertView.setEnabled(isEnabled(position)); + holder.setEnabled(isEnabled(position)); } - - mActive.remove(convertView); - mActive.add(convertView); - return convertView; + holder.itemView.setOnClickListener(mManageApplications); } - @VisibleForTesting - void updateDisableView(TextView view, ApplicationInfo info) { - if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { - view.setVisibility(View.VISIBLE); - view.setText(R.string.not_installed); - } else if (!info.enabled || info.enabledSetting - == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { - view.setVisibility(View.VISIBLE); - view.setText(R.string.disabled); - } else { - view.setVisibility(View.GONE); - } - } - - private void updateSummary(AppViewHolder holder) { + private void updateSummary(ApplicationViewHolder holder, AppEntry entry) { switch (mManageApplications.mListType) { case LIST_TYPE_NOTIFICATION: - if (holder.entry.extraInfo != null) { - holder.summary.setText(InstalledAppDetails.getNotificationSummary( - (AppRow) holder.entry.extraInfo, mContext)); + if (entry.extraInfo != null) { + holder.setSummary(InstalledAppDetails.getNotificationSummary( + (AppRow) entry.extraInfo, mContext)); } else { - holder.summary.setText(null); + holder.setSummary(null); } break; - case LIST_TYPE_USAGE_ACCESS: - if (holder.entry.extraInfo != null) { - holder.summary.setText((new UsageState((PermissionState) holder.entry - .extraInfo)).isPermissible() - ? R.string.app_permission_summary_allowed - : R.string.app_permission_summary_not_allowed); + if (entry.extraInfo != null) { + holder.setSummary( + (new UsageState((PermissionState) entry.extraInfo)).isPermissible() + ? R.string.app_permission_summary_allowed + : R.string.app_permission_summary_not_allowed); } else { - holder.summary.setText(null); + holder.setSummary(null); } break; - case LIST_TYPE_HIGH_POWER: - holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry)); + holder.setSummary(HighPowerDetail.getSummary(mContext, entry)); break; - case LIST_TYPE_OVERLAY: - holder.summary.setText(DrawOverlayDetails.getSummary(mContext, holder.entry)); + holder.setSummary(DrawOverlayDetails.getSummary(mContext, entry)); break; - case LIST_TYPE_WRITE_SETTINGS: - holder.summary.setText(WriteSettingsDetails.getSummary(mContext, - holder.entry)); + holder.setSummary(WriteSettingsDetails.getSummary(mContext, entry)); break; - case LIST_TYPE_MANAGE_SOURCES: - holder.summary.setText(ExternalSourcesDetails.getPreferenceSummary(mContext, - holder.entry)); + holder.setSummary(ExternalSourcesDetails.getPreferenceSummary(mContext, entry)); break; - default: - holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); + holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); break; } } - @Override - public Filter getFilter() { - return mFilter; - } - - @Override - public void onMovedToScrapHeap(View view) { - mActive.remove(view); - } - - @Override - public Object[] getSections() { - return mSections; - } - - @Override - public int getPositionForSection(int sectionIndex) { - return mSections[sectionIndex].position; - } - - @Override - public int getSectionForPosition(int position) { - return mPositionToSectionIndex[position]; + private boolean hasExtraView() { + return mExtraViewController != null + && mExtraViewController.shouldShow(); } } @@ -1439,21 +1253,6 @@ public class ManageApplications extends InstrumentedPreferenceFragment } } - private static class SectionInfo { - final String label; - final int position; - - public SectionInfo(String label, int position) { - this.label = label; - this.position = position; - } - - @Override - public String toString() { - return label; - } - } - public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY = new SummaryLoader.SummaryProviderFactory() { @Override diff --git a/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java b/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java index 1e4404723ed..2a2ac3b85e1 100644 --- a/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java +++ b/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java @@ -72,11 +72,11 @@ public class MusicViewHolderController implements FileViewHolderController { } @Override - public void setupView(AppViewHolder holder) { - holder.appIcon.setImageDrawable( + public void setupView(ApplicationViewHolder holder) { + holder.setIcon( new InsetDrawable(mContext.getDrawable(R.drawable.ic_headset_24dp), INSET_SIZE)); - holder.appName.setText(mContext.getText(R.string.audio_files_title)); - holder.summary.setText(Formatter.formatFileSize(mContext, mMusicSize)); + holder.setTitle(mContext.getText(R.string.audio_files_title)); + holder.setSummary(Formatter.formatFileSize(mContext, mMusicSize)); } @Override diff --git a/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java b/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java index 2d77f264a9a..fafa875a31c 100644 --- a/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java +++ b/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java @@ -71,11 +71,11 @@ public class PhotosViewHolderController implements FileViewHolderController { } @Override - public void setupView(AppViewHolder holder) { - holder.appIcon.setImageDrawable( + public void setupView(ApplicationViewHolder holder) { + holder.setIcon( new InsetDrawable(mContext.getDrawable(R.drawable.ic_photo_library), INSET_SIZE)); - holder.appName.setText(mContext.getText(R.string.storage_detail_images)); - holder.summary.setText(Formatter.formatFileSize(mContext, mFilesSize)); + holder.setTitle(mContext.getText(R.string.storage_detail_images)); + holder.setSummary(Formatter.formatFileSize(mContext, mFilesSize)); } @Override diff --git a/src/com/android/settings/development/AppPicker.java b/src/com/android/settings/development/AppPicker.java index d038b8fd765..433f31a23ef 100644 --- a/src/com/android/settings/development/AppPicker.java +++ b/src/com/android/settings/development/AppPicker.java @@ -32,7 +32,6 @@ import android.widget.ArrayAdapter; import android.widget.ListView; import com.android.settings.R; -import com.android.settings.applications.manageapplications.AppViewHolder; import java.text.Collator; import java.util.ArrayList; diff --git a/src/com/android/settings/applications/manageapplications/AppViewHolder.java b/src/com/android/settings/development/AppViewHolder.java similarity index 71% rename from src/com/android/settings/applications/manageapplications/AppViewHolder.java rename to src/com/android/settings/development/AppViewHolder.java index 7b9d76688dc..7a90bcf153c 100644 --- a/src/com/android/settings/applications/manageapplications/AppViewHolder.java +++ b/src/com/android/settings/development/AppViewHolder.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.android.settings.applications.manageapplications; +package com.android.settings.development; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -57,24 +56,4 @@ public class AppViewHolder { return (AppViewHolder)convertView.getTag(); } } - - void updateSizeText(CharSequence invalidSizeStr, int whichSize) { - if (ManageApplications.DEBUG) Log.i(ManageApplications.TAG, "updateSizeText of " - + entry.label + " " + entry + ": " + entry.sizeStr); - if (entry.sizeStr != null) { - switch (whichSize) { - case ManageApplications.SIZE_INTERNAL: - summary.setText(entry.internalSizeStr); - break; - case ManageApplications.SIZE_EXTERNAL: - summary.setText(entry.externalSizeStr); - break; - default: - summary.setText(entry.sizeStr); - break; - } - } else if (entry.size == ApplicationsState.SIZE_INVALID) { - summary.setText(invalidSizeStr); - } - } } \ No newline at end of file diff --git a/src/com/android/settings/display/AppGridView.java b/src/com/android/settings/display/AppGridView.java index 2d52ef2ca5a..148ba83064f 100644 --- a/src/com/android/settings/display/AppGridView.java +++ b/src/com/android/settings/display/AppGridView.java @@ -16,8 +16,6 @@ package com.android.settings.display; -import com.android.settings.R; - import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -30,6 +28,8 @@ import android.widget.ArrayAdapter; import android.widget.GridView; import android.widget.ImageView; +import com.android.settings.R; + import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java new file mode 100644 index 00000000000..cf2403b5b3d --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java @@ -0,0 +1,95 @@ +/* + * 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.applications.manageapplications; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.ApplicationsState; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ApplicationViewHolderTest { + + private Context mContext; + private View mView; + private ApplicationViewHolder mHolder; + + @Before + public void seUp() { + mContext = RuntimeEnvironment.application; + mView = ApplicationViewHolder.newView(LayoutInflater.from(mContext), + new FrameLayout(mContext)); + mHolder = new ApplicationViewHolder(mView); + } + + @Test + public void updateDisableView_appDisabledUntilUsed_shouldSetDisabled() { + final ApplicationInfo info = new ApplicationInfo(); + info.flags = ApplicationInfo.FLAG_INSTALLED; + info.enabled = true; + info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; + mHolder.updateDisableView(info); + + assertThat(mHolder.mDisabled.getText()).isEqualTo(mContext.getText(R.string.disabled)); + } + + @Test + public void setSummaries() { + mHolder.setSummary("hello"); + assertThat(mHolder.mSummary.getText()).isEqualTo("hello"); + + mHolder.setSummary(R.string.disabled); + assertThat(mHolder.mSummary.getText()).isEqualTo(mContext.getText(R.string.disabled)); + } + + @Test + public void updateSize() { + final String invalidStr = "invalid"; + final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class); + entry.internalSizeStr = "internal"; + entry.externalSizeStr = "external"; + entry.sizeStr = entry.internalSizeStr; + + mHolder.updateSizeText(entry, invalidStr, ManageApplications.SIZE_INTERNAL); + assertThat(mHolder.mSummary.getText()).isEqualTo(entry.internalSizeStr); + + mHolder.updateSizeText(entry, invalidStr, ManageApplications.SIZE_EXTERNAL); + assertThat(mHolder.mSummary.getText()).isEqualTo(entry.externalSizeStr); + + entry.sizeStr = null; + entry.size = ApplicationsState.SIZE_INVALID; + mHolder.updateSizeText(entry, invalidStr, ManageApplications.SIZE_EXTERNAL); + assertThat(mHolder.mSummary.getText()).isEqualTo(invalidStr); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java index 9afaf1f8a52..9eb36ef8d30 100644 --- a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java +++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java @@ -31,19 +31,16 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Handler; +import android.os.Bundle; import android.os.Looper; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import com.android.settings.R; -import com.android.settings.Settings; import com.android.settings.TestConfig; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; @@ -102,30 +99,6 @@ public class ManageApplicationsTest { ReflectionHelpers.setField(mFragment, "mLifecycle", new Lifecycle()); } - @Test - public void launchFragment() { - SettingsRobolectricTestRunner.startSettingsFragment( - mFragment, Settings.ManageApplicationsActivity.class); - } - - @Test - public void updateDisableView_appDisabledUntilUsed_shouldSetDisabled() { - final TextView view = mock(TextView.class); - final ApplicationInfo info = new ApplicationInfo(); - info.flags = ApplicationInfo.FLAG_INSTALLED; - info.enabled = true; - info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; - ManageApplications fragment = mock(ManageApplications.class); - when(fragment.getActivity()).thenReturn(mock(Activity.class)); - final ManageApplications.ApplicationsAdapter adapter = - new ManageApplications.ApplicationsAdapter(mState, fragment, - AppFilterRegistry.getInstance().get(FILTER_APPS_ALL)); - - adapter.updateDisableView(view, info); - - verify(view).setText(R.string.disabled); - } - @Test public void updateMenu_mainListType_showAppReset() { setUpOptionMenus(); @@ -170,14 +143,12 @@ public class ManageApplicationsTest { ReflectionHelpers.setField(fragment, "mLoadingContainer", mock(View.class)); ReflectionHelpers.setField(fragment, "mListContainer", mock(View.class)); when(fragment.getActivity()).thenReturn(mock(Activity.class)); - final Handler handler = mock(Handler.class); final ManageApplications.ApplicationsAdapter adapter = - spy(new ManageApplications.ApplicationsAdapter(mState, fragment, - AppFilterRegistry.getInstance().get(FILTER_APPS_ALL))); + spy(new ManageApplications.ApplicationsAdapter(mState, fragment, + AppFilterRegistry.getInstance().get(FILTER_APPS_ALL), new Bundle())); final LoadingViewController loadingViewController = mock(LoadingViewController.class); ReflectionHelpers.setField(adapter, "mLoadingViewController", loadingViewController); - ReflectionHelpers.setField(adapter, "mFgHandler", handler); // app loading completed ReflectionHelpers.setField(adapter, "mHasReceivedLoadEntries", true); @@ -196,15 +167,12 @@ public class ManageApplicationsTest { ReflectionHelpers.setField(fragment, "mLoadingContainer", mock(View.class)); ReflectionHelpers.setField(fragment, "mListContainer", mock(View.class)); when(fragment.getActivity()).thenReturn(mock(Activity.class)); - - final Handler handler = mock(Handler.class); final ManageApplications.ApplicationsAdapter adapter = - spy(new ManageApplications.ApplicationsAdapter(mState, fragment, - AppFilterRegistry.getInstance().get(FILTER_APPS_ALL))); + spy(new ManageApplications.ApplicationsAdapter(mState, fragment, + AppFilterRegistry.getInstance().get(FILTER_APPS_ALL), new Bundle())); final LoadingViewController loadingViewController = mock(LoadingViewController.class); ReflectionHelpers.setField(adapter, "mLoadingViewController", loadingViewController); - ReflectionHelpers.setField(adapter, "mFgHandler", handler); // app loading not yet completed ReflectionHelpers.setField(adapter, "mHasReceivedLoadEntries", false); @@ -218,6 +186,10 @@ public class ManageApplicationsTest { public void onRebuildComplete_shouldHideLoadingView() { final Context context = RuntimeEnvironment.application; final ManageApplications fragment = mock(ManageApplications.class); + final RecyclerView recyclerView = mock(RecyclerView.class); + final View emptyView = mock(View.class); + ReflectionHelpers.setField(fragment, "mRecyclerView", recyclerView); + ReflectionHelpers.setField(fragment, "mEmptyView", emptyView); final View loadingContainer = mock(View.class); when(loadingContainer.getContext()).thenReturn(context); final View listContainer = mock(View.class); @@ -226,14 +198,12 @@ public class ManageApplicationsTest { ReflectionHelpers.setField(fragment, "mLoadingContainer", loadingContainer); ReflectionHelpers.setField(fragment, "mListContainer", listContainer); when(fragment.getActivity()).thenReturn(mock(Activity.class)); - final Handler handler = mock(Handler.class); final ManageApplications.ApplicationsAdapter adapter = - spy(new ManageApplications.ApplicationsAdapter(mState, fragment, - AppFilterRegistry.getInstance().get(FILTER_APPS_ALL))); + spy(new ManageApplications.ApplicationsAdapter(mState, fragment, + AppFilterRegistry.getInstance().get(FILTER_APPS_ALL), new Bundle())); final LoadingViewController loadingViewController = mock(LoadingViewController.class); ReflectionHelpers.setField(adapter, "mLoadingViewController", loadingViewController); - ReflectionHelpers.setField(adapter, "mFgHandler", handler); ReflectionHelpers.setField(adapter, "mAppFilter", AppFilterRegistry.getInstance().get(FILTER_APPS_ALL)); diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java index 46a059a6f04..28ac9d97094 100644 --- a/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java @@ -28,11 +28,12 @@ import android.os.UserHandle; import android.os.storage.VolumeInfo; import android.provider.DocumentsContract; import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; import com.android.settings.TestConfig; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.StorageStatsSource; -import com.android.settingslib.deviceinfo.StorageVolumeProvider; import org.junit.Before; import org.junit.Test; @@ -51,14 +52,13 @@ public class MusicViewHolderControllerTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Fragment mFragment; @Mock - private StorageVolumeProvider mSvp; - @Mock private StorageStatsSource mSource; private Context mContext; private MusicViewHolderController mController; private VolumeInfo mVolume; - private AppViewHolder mHolder; + private View mView; + private ApplicationViewHolder mHolder; @Before public void setUp() throws Exception { @@ -69,25 +69,26 @@ public class MusicViewHolderControllerTest { new UserHandle(0)); LayoutInflater inflater = LayoutInflater.from(mContext); - mHolder = AppViewHolder.createOrRecycle(inflater, null); + mView = ApplicationViewHolder.newView(inflater, new FrameLayout(mContext)); + mHolder = new ApplicationViewHolder(mView); } @Test public void storageShouldBeZeroBytesIfQueriedBeforeStorageQueryFinishes() { mController.setupView(mHolder); - assertThat(mHolder.summary.getText().toString()).isEqualTo("0.00 B"); + assertThat(mHolder.mSummary.getText().toString()).isEqualTo("0.00 B"); } @Test public void storageShouldRepresentStorageStatsQuery() throws Exception { - when(mSource.getExternalStorageStats(nullable(String.class), nullable(UserHandle.class))).thenReturn( - new StorageStatsSource.ExternalStorageStats(1, 1, 0, 0, 0)); + when(mSource.getExternalStorageStats(nullable(String.class), nullable(UserHandle.class))) + .thenReturn(new StorageStatsSource.ExternalStorageStats(1, 1, 0, 0, 0)); mController.queryStats(); mController.setupView(mHolder); - assertThat(mHolder.summary.getText().toString()).isEqualTo("1.00 B"); + assertThat(mHolder.mSummary.getText().toString()).isEqualTo("1.00 B"); } @Test diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java index a051ac34088..2a26e312557 100644 --- a/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java @@ -27,11 +27,12 @@ import android.content.Intent; import android.os.UserHandle; import android.os.storage.VolumeInfo; import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; import com.android.settings.TestConfig; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.StorageStatsSource; -import com.android.settingslib.deviceinfo.StorageVolumeProvider; import org.junit.Before; import org.junit.Test; @@ -48,14 +49,14 @@ import org.robolectric.annotation.Config; public class PhotosViewHolderControllerTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Fragment mFragment; - - @Mock private StorageVolumeProvider mSvp; @Mock private StorageStatsSource mSource; private Context mContext; private PhotosViewHolderController mController; private VolumeInfo mVolume; - private AppViewHolder mHolder; + private View mView; + private ApplicationViewHolder mHolder; + @Before public void setUp() throws Exception { @@ -66,15 +67,16 @@ public class PhotosViewHolderControllerTest { new PhotosViewHolderController( mContext, mSource, mVolume.fsUuid, new UserHandle(0)); - LayoutInflater inflater = LayoutInflater.from(mContext); - mHolder = AppViewHolder.createOrRecycle(inflater, null); + final LayoutInflater inflater = LayoutInflater.from(mContext); + mView = ApplicationViewHolder.newView(inflater, new FrameLayout(mContext)); + mHolder = new ApplicationViewHolder(mView); } @Test public void storageShouldBeZeroBytesIfQueriedBeforeStorageQueryFinishes() { mController.setupView(mHolder); - assertThat(mHolder.summary.getText().toString()).isEqualTo("0.00 B"); + assertThat(mHolder.mSummary.getText().toString()).isEqualTo("0.00 B"); } @Test @@ -85,7 +87,7 @@ public class PhotosViewHolderControllerTest { mController.queryStats(); mController.setupView(mHolder); - assertThat(mHolder.summary.getText().toString()).isEqualTo("11.00 B"); + assertThat(mHolder.mSummary.getText().toString()).isEqualTo("11.00 B"); } @Test