diff options
11 files changed, 239 insertions, 2 deletions
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java index c5726d9eb..079f33244 100644 --- a/src/com/android/documentsui/BaseActivity.java +++ b/src/com/android/documentsui/BaseActivity.java @@ -434,6 +434,10 @@ public abstract class BaseActivity updateHeaderTitle(); } + protected ProfileTabsAddons getProfileTabsAddon() { + return mNavigator.getProfileTabsAddons(); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { diff --git a/src/com/android/documentsui/DummyProfileTabsAddons.java b/src/com/android/documentsui/DummyProfileTabsAddons.java new file mode 100644 index 000000000..697025e23 --- /dev/null +++ b/src/com/android/documentsui/DummyProfileTabsAddons.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 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.documentsui; + +/** + * A dummy {@ProfileTabsAddons} implementation. + */ +public class DummyProfileTabsAddons implements ProfileTabsAddons { + + @Override + public void setEnabled(boolean enabled) { + // Do nothing. + } +} diff --git a/src/com/android/documentsui/Injector.java b/src/com/android/documentsui/Injector.java index d6c631247..5761566c1 100644 --- a/src/com/android/documentsui/Injector.java +++ b/src/com/android/documentsui/Injector.java @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.view.MenuItem; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.recyclerview.selection.SelectionTracker; import androidx.recyclerview.widget.RecyclerView; @@ -36,7 +37,6 @@ import com.android.documentsui.prefs.ScopedPreferences; import com.android.documentsui.queries.SearchViewManager; import com.android.documentsui.ui.DialogController; import com.android.documentsui.ui.MessageBuilder; -import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -68,6 +68,9 @@ public class Injector<T extends ActionHandler> { public ActionModeController actionModeController; @ContentScoped + public ProfileTabsController profileTabsController; + + @ContentScoped public T actions; @ContentScoped diff --git a/src/com/android/documentsui/NavigationViewManager.java b/src/com/android/documentsui/NavigationViewManager.java index aca8c31b5..ed4acf6aa 100644 --- a/src/com/android/documentsui/NavigationViewManager.java +++ b/src/com/android/documentsui/NavigationViewManager.java @@ -106,6 +106,10 @@ public class NavigationViewManager { mSearchBarView.setOnClickListener(listener); } + public ProfileTabsAddons getProfileTabsAddons() { + return mProfileTabs; + } + private void onNavigationIconClicked() { if (mDrawer.isPresent()) { mDrawer.setOpen(true); diff --git a/src/com/android/documentsui/ProfileTabs.java b/src/com/android/documentsui/ProfileTabs.java index 24a36b2b7..f9e195ba5 100644 --- a/src/com/android/documentsui/ProfileTabs.java +++ b/src/com/android/documentsui/ProfileTabs.java @@ -19,6 +19,7 @@ package com.android.documentsui; import static androidx.core.util.Preconditions.checkNotNull; import android.view.View; +import android.view.ViewGroup; import androidx.annotation.VisibleForTesting; @@ -33,7 +34,8 @@ import java.util.List; /** * A manager class to control UI on a {@link TabLayout} for cross-profile purpose. */ -public class ProfileTabs { +public class ProfileTabs implements ProfileTabsAddons { + private static final float DISABLED_TAB_OPACITY = 0.38f; private final TabLayout mTabs; private final State mState; @@ -103,4 +105,20 @@ public class ProfileTabs { private TabLayout.Tab createTab(int resId, UserId userId) { return mTabs.newTab().setText(resId).setTag(userId); } + + @Override + public void setEnabled(boolean enabled) { + if (mTabs.getChildCount() > 0) { + View view = mTabs.getChildAt(0); + if (view instanceof ViewGroup) { + ViewGroup tabs = (ViewGroup) view; + for (int i = 0; i < tabs.getChildCount(); i++) { + View tabView = tabs.getChildAt(i); + tabView.setEnabled(enabled); + tabView.setAlpha((enabled || mTabs.getSelectedTabPosition() == i) ? 1f + : DISABLED_TAB_OPACITY); + } + } + } + } } diff --git a/src/com/android/documentsui/ProfileTabsAddons.java b/src/com/android/documentsui/ProfileTabsAddons.java new file mode 100644 index 000000000..885d29828 --- /dev/null +++ b/src/com/android/documentsui/ProfileTabsAddons.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 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.documentsui; + +/** + * Provides methods to control a {@link ProfileTabs}. + */ +public interface ProfileTabsAddons { + /** + * Enables/Disables the profile tabs. + */ + void setEnabled(boolean enabled); +} diff --git a/src/com/android/documentsui/ProfileTabsController.java b/src/com/android/documentsui/ProfileTabsController.java new file mode 100644 index 000000000..d10806b4f --- /dev/null +++ b/src/com/android/documentsui/ProfileTabsController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 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.documentsui; + +import androidx.recyclerview.selection.SelectionTracker; +import androidx.recyclerview.selection.SelectionTracker.SelectionObserver; + +/** + * A controller that listens to selection changes and control profile tabs behavior. + */ +public class ProfileTabsController extends SelectionObserver<String> { + + private static final String TAG = "ProfileTabsController"; + + private final SelectionTracker<String> mSelectionMgr; + private final ProfileTabsAddons mProfileTabsAddons; + + public ProfileTabsController( + SelectionTracker<String> selectionMgr, + ProfileTabsAddons profileTabsAddons) { + mSelectionMgr = selectionMgr; + mProfileTabsAddons = profileTabsAddons; + } + + @Override + public void onSelectionChanged() { + mProfileTabsAddons.setEnabled(mSelectionMgr.getSelection().isEmpty()); + } + + @Override + public void onSelectionRestored() { + onSelectionChanged(); + } +} diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java index d82fdeb52..dcd2edd19 100644 --- a/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -79,6 +79,7 @@ import com.android.documentsui.Injector.Injected; import com.android.documentsui.MetricConsts; import com.android.documentsui.Metrics; import com.android.documentsui.Model; +import com.android.documentsui.ProfileTabsController; import com.android.documentsui.R; import com.android.documentsui.ThumbnailCache; import com.android.documentsui.base.DocumentFilters; @@ -155,6 +156,10 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On @ContentScoped private ActionModeController mActionModeController; + @Injected + @ContentScoped + private ProfileTabsController mProfileTabsController; + private DocDetailsLookup mDetailsLookup; private SelectionMetadata mSelectionMetadata; private KeyInputHandler mKeyListener; @@ -402,6 +407,9 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On mSelectionMgr.addObserver(mActionModeController); + mProfileTabsController = mInjector.profileTabsController; + mSelectionMgr.addObserver(mProfileTabsController); + final ActivityManager am = (ActivityManager) mActivity.getSystemService( Context.ACTIVITY_SERVICE); boolean svelte = am.isLowRamDevice() && (mState.stack.isRecents()); diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java index 81ff61ff7..1d8909fbd 100644 --- a/src/com/android/documentsui/files/FilesActivity.java +++ b/src/com/android/documentsui/files/FilesActivity.java @@ -37,11 +37,14 @@ import com.android.documentsui.ActionModeController; import com.android.documentsui.BaseActivity; import com.android.documentsui.DocsSelectionHelper; import com.android.documentsui.DocumentsApplication; +import com.android.documentsui.DummyProfileTabsAddons; import com.android.documentsui.FocusManager; import com.android.documentsui.Injector; import com.android.documentsui.MenuManager.DirectoryDetails; import com.android.documentsui.OperationDialogFragment; import com.android.documentsui.OperationDialogFragment.DialogType; +import com.android.documentsui.ProfileTabsAddons; +import com.android.documentsui.ProfileTabsController; import com.android.documentsui.ProviderExecutor; import com.android.documentsui.R; import com.android.documentsui.SharedInputHandler; @@ -74,6 +77,7 @@ public class FilesActivity extends BaseActivity implements AbstractActionHandler private Injector<ActionHandler<FilesActivity>> mInjector; private ActivityInputHandler mActivityInputHandler; private SharedInputHandler mSharedInputHandler; + private final ProfileTabsAddons mProfileTabsAddonsStub = new DummyProfileTabsAddons(); public FilesActivity() { super(R.layout.files_activity, TAG); @@ -148,6 +152,12 @@ public class FilesActivity extends BaseActivity implements AbstractActionHandler mInjector.searchManager = mSearchManager; + // No profile tabs will be shown on FilesActivity. Use a dummy to avoid unnecessary + // operations. + mInjector.profileTabsController = new ProfileTabsController( + mInjector.selectionMgr, + mProfileTabsAddonsStub); + mAppsRowManager = new AppsRowManager(mInjector.actions); mInjector.appsRowManager = mAppsRowManager; diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java index f7f2ebe80..f1a33098f 100644 --- a/src/com/android/documentsui/picker/PickActivity.java +++ b/src/com/android/documentsui/picker/PickActivity.java @@ -46,6 +46,7 @@ import com.android.documentsui.FocusManager; import com.android.documentsui.Injector; import com.android.documentsui.MenuManager.DirectoryDetails; import com.android.documentsui.Metrics; +import com.android.documentsui.ProfileTabsController; import com.android.documentsui.ProviderExecutor; import com.android.documentsui.R; import com.android.documentsui.SharedInputHandler; @@ -121,6 +122,10 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons { mInjector.menuManager, mInjector.messages); + mInjector.profileTabsController = new ProfileTabsController( + mInjector.selectionMgr, + getProfileTabsAddon()); + mInjector.pickResult = getPickResult(icicle); mInjector.actions = new ActionHandler<>( this, diff --git a/tests/unit/com/android/documentsui/ProfileTabsControllerTest.java b/tests/unit/com/android/documentsui/ProfileTabsControllerTest.java new file mode 100644 index 000000000..2c6b2f90e --- /dev/null +++ b/tests/unit/com/android/documentsui/ProfileTabsControllerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 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.documentsui; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.recyclerview.selection.SelectionTracker; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +@SmallTest +public class ProfileTabsControllerTest { + + private SelectionTracker<String> mSelectionMgr = SelectionHelpers.createTestInstance(); + private Boolean testEnabledValue = null; + private ProfileTabsController mController = new ProfileTabsController(mSelectionMgr, + enabled -> testEnabledValue = enabled); + + @Before + public void setUp() throws Exception { + mSelectionMgr.addObserver(mController); + } + + @Test + public void testNoSelection_enable() { + mController.onSelectionRestored(); + assertThat(testEnabledValue).isTrue(); + } + + @Test + public void testClearSelection_enable() { + mSelectionMgr.select("foo"); + mSelectionMgr.clearSelection(); + assertThat(testEnabledValue).isTrue(); + } + + @Test + public void testDeselectAll_enable() { + mSelectionMgr.select("foo"); + mSelectionMgr.select("bar"); + mSelectionMgr.deselect("foo"); + mSelectionMgr.deselect("bar"); + assertThat(testEnabledValue).isTrue(); + } + + @Test + public void testSelection_disable() { + mSelectionMgr.select("foo"); + assertThat(testEnabledValue).isFalse(); + } + + @Test + public void testMultipleSelection_disable() { + mSelectionMgr.select("foo"); + mSelectionMgr.select("bar"); + mSelectionMgr.select("apple"); + assertThat(testEnabledValue).isFalse(); + } + + @Test + public void testDeselectSome_Disable() { + mSelectionMgr.select("foo"); + mSelectionMgr.select("bar"); + mSelectionMgr.deselect("bar"); + assertThat(testEnabledValue).isFalse(); + } +} |