summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael W <baddaemon87@gmail.com>2020-06-14 11:20:35 +0200
committerMichael Bestas <mkbestas@lineageos.org>2020-12-08 19:39:12 +0200
commit9ef3d79e043ad33934616b710eb201f032c539b8 (patch)
treef521336734e1b2ec8836c24d75b64c70d7d43b14
parent248c6793c4892b8444d64e89c221533270b2946a (diff)
DeskClock: Replace ViewPager with manual fragment handling
Change-Id: Iaadfb417f0ca8638936875113ec2f39853f2a39c
-rw-r--r--Android.bp3
-rw-r--r--proguard.flags1
-rw-r--r--res/layout/clock_fragment.xml3
-rw-r--r--res/layout/desk_clock.xml43
-rw-r--r--src/com/android/deskclock/AlarmClockFragment.java10
-rw-r--r--src/com/android/deskclock/ClockFragment.java5
-rw-r--r--src/com/android/deskclock/DeskClock.java165
-rw-r--r--src/com/android/deskclock/DeskClockFragment.java2
-rw-r--r--src/com/android/deskclock/FabController.java8
-rw-r--r--src/com/android/deskclock/FragmentTabPagerAdapter.java167
-rw-r--r--src/com/android/deskclock/FragmentUtils.java113
-rw-r--r--src/com/android/deskclock/stopwatch/StopwatchFragment.java5
-rw-r--r--src/com/android/deskclock/timer/TimerFragment.java20
-rw-r--r--src/com/android/deskclock/uidata/TabModel.java2
14 files changed, 213 insertions, 334 deletions
diff --git a/Android.bp b/Android.bp
index 2d27ae94a..0e49ab5f1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,9 @@ android_app {
resource_dirs: ["res"],
sdk_version: "current",
overrides: ["AlarmClock"],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
srcs: [
"src/**/*.java",
"gen/**/*.java",
diff --git a/proguard.flags b/proguard.flags
new file mode 100644
index 000000000..a6832665d
--- /dev/null
+++ b/proguard.flags
@@ -0,0 +1 @@
+-keep class androidx.viewpager.widget.* { *; }
diff --git a/res/layout/clock_fragment.xml b/res/layout/clock_fragment.xml
index ec97381a4..d1b7619f2 100644
--- a/res/layout/clock_fragment.xml
+++ b/res/layout/clock_fragment.xml
@@ -18,9 +18,8 @@
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/cities"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_weight="1"
android:clickable="false"
android:clipToPadding="false"
android:paddingBottom="@dimen/fab_height"
diff --git a/res/layout/desk_clock.xml b/res/layout/desk_clock.xml
index 76ea224c3..e39d1bbbd 100644
--- a/res/layout/desk_clock.xml
+++ b/res/layout/desk_clock.xml
@@ -25,25 +25,19 @@
app:statusBarBackground="@null"
android:fitsSystemWindows="true">
- <androidx.coordinatorlayout.widget.CoordinatorLayout
- android:id="@+id/content"
+ <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1">
+ android:layout_height="wrap_content"
+ android:background="@null"
+ app:elevation="0dp">
- <com.google.android.material.appbar.AppBarLayout
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@null"
- app:elevation="0dp">
-
- <androidx.appcompat.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:contentInsetStart="0dp"
- tools:ignore="RtlSymmetry"
- android:gravity="center">
+ app:contentInsetStart="0dp"
+ tools:ignore="RtlSymmetry"
+ android:gravity="center">
<TextView
android:id="@+id/title_view"
@@ -52,16 +46,19 @@
android:layout_gravity="center"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
- </androidx.appcompat.widget.Toolbar>
- </com.google.android.material.appbar.AppBarLayout>
+ </androidx.appcompat.widget.Toolbar>
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <androidx.coordinatorlayout.widget.CoordinatorLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
- <androidx.viewpager.widget.ViewPager
- android:id="@+id/desk_clock_pager"
+ <FrameLayout
+ android:id="@+id/fragment_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- android:saveEnabled="false"
- app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+ android:layout_height="match_parent"/>
<LinearLayout
android:layout_width="match_parent"
diff --git a/src/com/android/deskclock/AlarmClockFragment.java b/src/com/android/deskclock/AlarmClockFragment.java
index ac1966a0d..70ffee3a5 100644
--- a/src/com/android/deskclock/AlarmClockFragment.java
+++ b/src/com/android/deskclock/AlarmClockFragment.java
@@ -31,6 +31,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
+import androidx.loader.content.CursorLoader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -81,7 +82,7 @@ public final class AlarmClockFragment extends DeskClockFragment implements
private RecyclerView mRecyclerView;
// Data
- private Loader mCursorLoader;
+ private CursorLoader mCursorLoader;
private long mScrollToAlarmId = Alarm.INVALID_ID;
private long mExpandedAlarmId = Alarm.INVALID_ID;
private long mCurrentUpdateToken;
@@ -103,7 +104,7 @@ public final class AlarmClockFragment extends DeskClockFragment implements
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
- mCursorLoader = LoaderManager.getInstance(this).initLoader(0, null, this);
+ mCursorLoader = (CursorLoader) LoaderManager.getInstance(this).initLoader(0, null, this);
if (savedState != null) {
mExpandedAlarmId = savedState.getLong(KEY_EXPANDED_ID, Alarm.INVALID_ID);
}
@@ -402,6 +403,11 @@ public final class AlarmClockFragment extends DeskClockFragment implements
right.setVisibility(View.INVISIBLE);
}
+ @Override
+ public final int getFabTargetVisibility() {
+ return View.VISIBLE;
+ }
+
private void startCreatingAlarm() {
// Clear the currently selected alarm.
mAlarmTimeClickHandler.setSelectedAlarm(null);
diff --git a/src/com/android/deskclock/ClockFragment.java b/src/com/android/deskclock/ClockFragment.java
index 1536b55df..b487e52be 100644
--- a/src/com/android/deskclock/ClockFragment.java
+++ b/src/com/android/deskclock/ClockFragment.java
@@ -215,6 +215,11 @@ public final class ClockFragment extends DeskClockFragment {
right.setVisibility(INVISIBLE);
}
+ @Override
+ public final int getFabTargetVisibility() {
+ return View.VISIBLE;
+ }
+
/**
* Refresh the next alarm time.
*/
diff --git a/src/com/android/deskclock/DeskClock.java b/src/com/android/deskclock/DeskClock.java
index cc3687002..4dea5d269 100644
--- a/src/com/android/deskclock/DeskClock.java
+++ b/src/com/android/deskclock/DeskClock.java
@@ -88,9 +88,6 @@ public class DeskClock extends BaseActivity
/** Hides, updates, and shows only the {@link #mLeftButton} and {@link #mRightButton}. */
private final AnimatorSet mUpdateButtonsOnlyAnimation = new AnimatorSet();
- /** Automatically starts the {@link #mShowAnimation} after {@link #mHideAnimation} ends. */
- private final AnimatorListenerAdapter mAutoStartShowListener = new AutoStartShowListener();
-
/** Updates the user interface to reflect the selected tab from the backing model. */
private final TabListener mTabChangeWatcher = new TabChangeWatcher();
@@ -119,15 +116,14 @@ public class DeskClock extends BaseActivity
/** The ViewPager that pages through the fragments representing the content of the tabs. */
private ViewPager mFragmentTabPager;
- /** Generates the fragments that are displayed by the {@link #mFragmentTabPager}. */
- private FragmentTabPagerAdapter mFragmentTabPagerAdapter;
-
/** The view that displays the current tab's title */
private TextView mTitleView;
/** The bottom navigation bar */
private BottomNavigationView mBottomNavigation;
+ private FragmentUtils mFragmentUtils;
+
/** {@code true} when a settings change necessitates recreating this activity. */
private boolean mRecreateActivity;
@@ -248,18 +244,7 @@ public class DeskClock extends BaseActivity
.after(leftHideAnimation)
.after(rightHideAnimation);
- // Customize the view pager.
- mFragmentTabPagerAdapter = new FragmentTabPagerAdapter(this);
- mFragmentTabPager = (ViewPager) findViewById(R.id.desk_clock_pager);
- // Keep all four tabs to minimize jank.
- mFragmentTabPager.setOffscreenPageLimit(3);
- // Set Accessibility Delegate to null so view pager doesn't intercept movements and
- // prevent the fab from being selected.
- mFragmentTabPager.setAccessibilityDelegate(null);
- // Mirror changes made to the selected page of the view pager into UiDataModel.
- mFragmentTabPager.addOnPageChangeListener(new PageChangeWatcher());
- mFragmentTabPager.setAdapter(mFragmentTabPagerAdapter);
-
+ mFragmentUtils = new FragmentUtils(this);
// Mirror changes made to the selected tab into UiDataModel.
mBottomNavigation = findViewById(R.id.bottom_view);
mBottomNavigation.setOnNavigationItemSelectedListener(mNavigationListener);
@@ -275,27 +260,40 @@ public class DeskClock extends BaseActivity
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
- UiDataModel.Tab tab = null;
+ UiDataModel.Tab selectedTab = null;
switch (item.getItemId()) {
case R.id.page_alarm:
- tab = UiDataModel.Tab.ALARMS;
+ selectedTab = UiDataModel.Tab.ALARMS;
break;
case R.id.page_clock:
- tab = UiDataModel.Tab.CLOCKS;
+ selectedTab = UiDataModel.Tab.CLOCKS;
break;
case R.id.page_timer:
- tab = UiDataModel.Tab.TIMERS;
+ selectedTab = UiDataModel.Tab.TIMERS;
break;
case R.id.page_stopwatch:
- tab = UiDataModel.Tab.STOPWATCH;
+ selectedTab = UiDataModel.Tab.STOPWATCH;
break;
}
- if (tab != null) {
- UiDataModel.getUiDataModel().setSelectedTab(tab);
+ if (selectedTab != null) {
+ UiDataModel.Tab currentTab = UiDataModel.getUiDataModel().getSelectedTab();
+ DeskClockFragment currentFrag = mFragmentUtils.getDeskClockFragment(currentTab);
+ DeskClockFragment selectedFrag = mFragmentUtils.getDeskClockFragment(selectedTab);
+
+ int currentVisibility = currentFrag.getFabTargetVisibility();
+ int targetVisibility = selectedFrag.getFabTargetVisibility();
+ if (currentVisibility != targetVisibility) {
+ if (targetVisibility == View.VISIBLE) {
+ mShowAnimation.start();
+ } else {
+ mHideAnimation.start();
+ }
+ }
+ UiDataModel.getUiDataModel().setSelectedTab(selectedTab);
return true;
}
@@ -324,15 +322,6 @@ public class DeskClock extends BaseActivity
if (mRecreateActivity) {
mRecreateActivity = false;
-
- // A runnable must be posted here or the new DeskClock activity will be recreated in a
- // paused state, even though it is the foreground activity.
- mFragmentTabPager.post(new Runnable() {
- @Override
- public void run() {
- recreate();
- }
- });
}
}
@@ -468,16 +457,7 @@ public class DeskClock extends BaseActivity
final UiDataModel.Tab selectedTab = UiDataModel.getUiDataModel().getSelectedTab();
// Update the selected tab in the mBottomNavigation if it does not agree with UiDataModel.
mBottomNavigation.setSelectedItemId(selectedTab.getPageResId());
-
- // Update the selected fragment in the viewpager if it does not agree with UiDataModel.
- for (int i = 0; i < mFragmentTabPagerAdapter.getCount(); i++) {
- final DeskClockFragment fragment = mFragmentTabPagerAdapter.getDeskClockFragment(i);
- if (fragment.isTabSelected() && mFragmentTabPager.getCurrentItem() != i) {
- mFragmentTabPager.setCurrentItem(i);
- break;
- }
- }
-
+ mFragmentUtils.showFragment(selectedTab);
mTitleView.setText(selectedTab.getLabelResId());
}
@@ -485,14 +465,7 @@ public class DeskClock extends BaseActivity
* @return the DeskClockFragment that is currently selected according to UiDataModel
*/
private DeskClockFragment getSelectedDeskClockFragment() {
- for (int i = 0; i < mFragmentTabPagerAdapter.getCount(); i++) {
- final DeskClockFragment fragment = mFragmentTabPagerAdapter.getDeskClockFragment(i);
- if (fragment.isTabSelected()) {
- return fragment;
- }
- }
- final UiDataModel.Tab selectedTab = UiDataModel.getUiDataModel().getSelectedTab();
- throw new IllegalStateException("Unable to locate selected fragment (" + selectedTab + ")");
+ return mFragmentUtils.getCurrentFragment();
}
/**
@@ -503,93 +476,6 @@ public class DeskClock extends BaseActivity
}
/**
- * As the view pager changes the selected page, update the model to record the new selected tab.
- */
- private final class PageChangeWatcher implements OnPageChangeListener {
-
- /** The last reported page scroll state; used to detect exotic state changes. */
- private int mPriorState = SCROLL_STATE_IDLE;
-
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- // Only hide the fab when a non-zero drag distance is detected. This prevents
- // over-scrolling from needlessly hiding the fab.
- if (mFabState == FabState.HIDE_ARMED && positionOffsetPixels != 0) {
- mFabState = FabState.HIDING;
- mHideAnimation.start();
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- if (mPriorState == SCROLL_STATE_IDLE && state == SCROLL_STATE_SETTLING) {
- // The user has tapped a tab button; play the hide and show animations linearly.
- mHideAnimation.addListener(mAutoStartShowListener);
- mHideAnimation.start();
- mFabState = FabState.HIDING;
- } else if (mPriorState == SCROLL_STATE_SETTLING && state == SCROLL_STATE_DRAGGING) {
- // The user has interrupted settling on a tab and the fab button must be re-hidden.
- if (mShowAnimation.isStarted()) {
- mShowAnimation.cancel();
- }
- if (mHideAnimation.isStarted()) {
- // Let the hide animation finish naturally; don't auto show when it ends.
- mHideAnimation.removeListener(mAutoStartShowListener);
- } else {
- // Start and immediately end the hide animation to jump to the hidden state.
- mHideAnimation.start();
- mHideAnimation.end();
- }
- mFabState = FabState.HIDING;
-
- } else if (state != SCROLL_STATE_DRAGGING && mFabState == FabState.HIDING) {
- // The user has lifted their finger; show the buttons now or after hide ends.
- if (mHideAnimation.isStarted()) {
- // Finish the hide animation and then start the show animation.
- mHideAnimation.addListener(mAutoStartShowListener);
- } else {
- updateFab(FAB_AND_BUTTONS_IMMEDIATE);
- mShowAnimation.start();
-
- // The animation to show the fab has begun; update the state to showing.
- mFabState = FabState.SHOWING;
- }
- } else if (state == SCROLL_STATE_DRAGGING) {
- // The user has started a drag so arm the hide animation.
- mFabState = FabState.HIDE_ARMED;
- }
-
- // Update the last known state.
- mPriorState = state;
- }
-
- @Override
- public void onPageSelected(int position) {
- mFragmentTabPagerAdapter.getDeskClockFragment(position).selectTab();
- }
- }
-
- /**
- * If this listener is attached to {@link #mHideAnimation} when it ends, the corresponding
- * {@link #mShowAnimation} is automatically started.
- */
- private final class AutoStartShowListener extends AnimatorListenerAdapter {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Prepare the hide animation for its next use; by default do not auto-show after hide.
- mHideAnimation.removeListener(mAutoStartShowListener);
-
- // Update the buttons now that they are no longer visible.
- updateFab(FAB_AND_BUTTONS_IMMEDIATE);
-
- // Automatically start the grow animation now that shrinking is complete.
- mShowAnimation.start();
-
- // The animation to show the fab has begun; update the state to showing.
- mFabState = FabState.SHOWING;
- }
- }
-
- /**
* Shows/hides a snackbar as silencing settings are enabled/disabled.
*/
private final class SilentSettingChangeWatcher implements OnSilentSettingsListener {
@@ -666,6 +552,7 @@ public class DeskClock extends BaseActivity
// If the hide animation has already completed, the buttons must be updated now when the
// new tab is known. Otherwise they are updated at the end of the hide animation.
if (!mHideAnimation.isStarted()) {
+ getSupportFragmentManager().executePendingTransactions();
updateFab(FAB_AND_BUTTONS_IMMEDIATE);
}
}
diff --git a/src/com/android/deskclock/DeskClockFragment.java b/src/com/android/deskclock/DeskClockFragment.java
index 1f51a332e..c08a15a8e 100644
--- a/src/com/android/deskclock/DeskClockFragment.java
+++ b/src/com/android/deskclock/DeskClockFragment.java
@@ -20,6 +20,7 @@ import android.view.KeyEvent;
import android.widget.Button;
import android.widget.ImageView;
import androidx.annotation.ColorInt;
+import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
@@ -104,6 +105,7 @@ public abstract class DeskClockFragment extends Fragment implements FabContainer
/**
* Select the tab that displays this fragment.
*/
+ @Keep
public final void selectTab() {
UiDataModel.getUiDataModel().setSelectedTab(mTab);
}
diff --git a/src/com/android/deskclock/FabController.java b/src/com/android/deskclock/FabController.java
index bab7f46ad..11c5d5375 100644
--- a/src/com/android/deskclock/FabController.java
+++ b/src/com/android/deskclock/FabController.java
@@ -57,4 +57,10 @@ public interface FabController {
* @param right the button to the right of the fab component
*/
void onRightButtonClick(@NonNull Button right);
-} \ No newline at end of file
+
+ /**
+ *
+ * @return the target visibility of the FAB component
+ */
+ int getFabTargetVisibility();
+}
diff --git a/src/com/android/deskclock/FragmentTabPagerAdapter.java b/src/com/android/deskclock/FragmentTabPagerAdapter.java
deleted file mode 100644
index de3e25c35..000000000
--- a/src/com/android/deskclock/FragmentTabPagerAdapter.java
+++ /dev/null
@@ -1,167 +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.deskclock;
-
-import android.util.ArrayMap;
-import android.view.View;
-import android.view.ViewGroup;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
-import androidx.viewpager.widget.PagerAdapter;
-
-import com.android.deskclock.uidata.UiDataModel;
-
-import java.util.Map;
-
-/**
- * This adapter produces the DeskClockFragments that are the content of the DeskClock tabs. The
- * adapter presents the tabs in LTR and RTL order depending on the text layout direction for the
- * current locale. To prevent issues when switching between LTR and RTL, fragments are registered
- * with the manager using position-independent tags, which is an important departure from
- * FragmentPagerAdapter.
- */
-final class FragmentTabPagerAdapter extends PagerAdapter {
-
- private final DeskClock mDeskClock;
-
- /** The manager into which fragments are added. */
- private final FragmentManager mFragmentManager;
-
- /** A fragment cache that can be accessed before {@link #instantiateItem} is called. */
- private final Map<UiDataModel.Tab, DeskClockFragment> mFragmentCache;
-
- /** The active fragment transaction if one exists. */
- private FragmentTransaction mCurrentTransaction;
-
- /** The current fragment displayed to the user. */
- private Fragment mCurrentPrimaryItem;
-
- FragmentTabPagerAdapter(DeskClock deskClock) {
- mDeskClock = deskClock;
- mFragmentCache = new ArrayMap<>(getCount());
- mFragmentManager = deskClock.getSupportFragmentManager();
- }
-
- @Override
- public int getCount() {
- return UiDataModel.getUiDataModel().getTabCount();
- }
-
- /**
- * @param position the left-to-right index of the fragment to be returned
- * @return the fragment displayed at the given {@code position}
- */
- DeskClockFragment getDeskClockFragment(int position) {
- // Fetch the tab the UiDataModel reports for the position.
- final UiDataModel.Tab tab = UiDataModel.getUiDataModel().getTabAt(position);
-
- // First check the local cache for the fragment.
- DeskClockFragment fragment = mFragmentCache.get(tab);
- if (fragment != null) {
- return fragment;
- }
-
- // Next check the fragment manager; relevant when app is rebuilt after locale changes
- // because this adapter will be new and mFragmentCache will be empty, but the fragment
- // manager will retain the Fragments built on original application launch.
- fragment = (DeskClockFragment) mFragmentManager.findFragmentByTag(tab.name());
- if (fragment != null) {
- fragment.setFabContainer(mDeskClock);
- mFragmentCache.put(tab, fragment);
- return fragment;
- }
-
- // Otherwise, build the fragment from scratch.
- final String fragmentClassName = tab.getFragmentClassName();
- fragment = (DeskClockFragment) Fragment.instantiate(mDeskClock, fragmentClassName);
- fragment.setFabContainer(mDeskClock);
- mFragmentCache.put(tab, fragment);
- return fragment;
- }
-
- @Override
- public void startUpdate(ViewGroup container) {
- if (container.getId() == View.NO_ID) {
- throw new IllegalStateException("ViewPager with adapter " + this + " has no id");
- }
- }
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- if (mCurrentTransaction == null) {
- mCurrentTransaction = mFragmentManager.beginTransaction();
- }
-
- // Use the fragment located in the fragment manager if one exists.
- final UiDataModel.Tab tab = UiDataModel.getUiDataModel().getTabAt(position);
- Fragment fragment = mFragmentManager.findFragmentByTag(tab.name());
- if (fragment != null) {
- mCurrentTransaction.attach(fragment);
- } else {
- fragment = getDeskClockFragment(position);
- mCurrentTransaction.add(container.getId(), fragment, tab.name());
- }
-
- if (fragment != mCurrentPrimaryItem) {
- fragment.setMenuVisibility(false);
- fragment.setUserVisibleHint(false);
- }
-
- return fragment;
- }
-
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- if (mCurrentTransaction == null) {
- mCurrentTransaction = mFragmentManager.beginTransaction();
- }
- final DeskClockFragment fragment = (DeskClockFragment) object;
- fragment.setFabContainer(null);
- mCurrentTransaction.detach(fragment);
- }
-
- @Override
- public void setPrimaryItem(ViewGroup container, int position, Object object) {
- final Fragment fragment = (Fragment) object;
- if (fragment != mCurrentPrimaryItem) {
- if (mCurrentPrimaryItem != null) {
- mCurrentPrimaryItem.setMenuVisibility(false);
- mCurrentPrimaryItem.setUserVisibleHint(false);
- }
- if (fragment != null) {
- fragment.setMenuVisibility(true);
- fragment.setUserVisibleHint(true);
- }
- mCurrentPrimaryItem = fragment;
- }
- }
-
- @Override
- public void finishUpdate(ViewGroup container) {
- if (mCurrentTransaction != null) {
- mCurrentTransaction.commitAllowingStateLoss();
- mCurrentTransaction = null;
- mFragmentManager.executePendingTransactions();
- }
- }
-
- @Override
- public boolean isViewFromObject(View view, Object object) {
- return ((Fragment) object).getView() == view;
- }
-} \ No newline at end of file
diff --git a/src/com/android/deskclock/FragmentUtils.java b/src/com/android/deskclock/FragmentUtils.java
new file mode 100644
index 000000000..43d7a4dc7
--- /dev/null
+++ b/src/com/android/deskclock/FragmentUtils.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 The LineageOS 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.deskclock;
+
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.deskclock.uidata.UiDataModel;
+
+import java.util.Map;
+
+/**
+ * This class produces the DeskClockFragments that are the content of the DeskClock tabs.
+ * It presents the tabs in LTR and RTL order depending on the text layout direction for the
+ * current locale. To prevent issues when switching between LTR and RTL, fragments are registered
+ * with the manager using position-independent tags, which is an important departure from
+ * FragmentPagerAdapter.
+ */
+public final class FragmentUtils {
+
+ private final DeskClock mDeskClock;
+
+ /** The manager into which fragments are added. */
+ private final FragmentManager mFragmentManager;
+
+ /** A fragment cache that can be accessed before {@link #instantiateItem} is called. */
+ private final Map<UiDataModel.Tab, DeskClockFragment> mFragmentCache;
+
+ /** The current fragment displayed to the user. */
+ private DeskClockFragment mCurrentPrimaryItem;
+
+ public FragmentUtils(DeskClock deskClock) {
+ mDeskClock = deskClock;
+ mFragmentCache = new ArrayMap<>(getCount());
+ mFragmentManager = deskClock.getSupportFragmentManager();
+ }
+
+ private int getCount() {
+ return UiDataModel.getUiDataModel().getTabCount();
+ }
+
+ public DeskClockFragment getDeskClockFragment(UiDataModel.Tab tab) {
+ // First check the local cache for the fragment.
+ DeskClockFragment fragment = mFragmentCache.get(tab);
+ if (fragment != null) {
+ return fragment;
+ }
+
+ // Next check the fragment manager; relevant when app is rebuilt after locale changes
+ // because this adapter will be new and mFragmentCache will be empty, but the fragment
+ // manager will retain the Fragments built on original application launch.
+ fragment = (DeskClockFragment) mFragmentManager.findFragmentByTag(tab.name());
+ if (fragment != null) {
+ fragment.setFabContainer(mDeskClock);
+ mFragmentCache.put(tab, fragment);
+ return fragment;
+ }
+
+ // Otherwise, build the fragment from scratch.
+ final String fragmentClassName = tab.getFragmentClassName();
+ fragment = (DeskClockFragment) Fragment.instantiate(mDeskClock, fragmentClassName);
+ fragment.setFabContainer(mDeskClock);
+
+ FragmentTransaction transaction = mFragmentManager.beginTransaction();
+ transaction.add(R.id.fragment_container, fragment, tab.name());
+ transaction.commit();
+
+ mFragmentCache.put(tab, fragment);
+ return fragment;
+ }
+
+ public void hideAllFragments() {
+ FragmentTransaction transaction = mFragmentManager.beginTransaction();
+ for (UiDataModel.Tab tab : UiDataModel.Tab.values()) {
+ Fragment fragment = mFragmentManager.findFragmentByTag(tab.name());
+ if (fragment != null) {
+ transaction.hide(fragment);
+ }
+ }
+
+ transaction.commit();
+ }
+
+ public void showFragment(UiDataModel.Tab tab) {
+ hideAllFragments();
+ Fragment fragment = getDeskClockFragment(tab);
+ mFragmentManager.beginTransaction().show(fragment).commit();
+ mCurrentPrimaryItem = (DeskClockFragment) fragment;
+ }
+
+ public DeskClockFragment getCurrentFragment() {
+ return mCurrentPrimaryItem;
+ }
+} \ No newline at end of file
diff --git a/src/com/android/deskclock/stopwatch/StopwatchFragment.java b/src/com/android/deskclock/stopwatch/StopwatchFragment.java
index d0b2466fe..87d22ea60 100644
--- a/src/com/android/deskclock/stopwatch/StopwatchFragment.java
+++ b/src/com/android/deskclock/stopwatch/StopwatchFragment.java
@@ -294,6 +294,11 @@ public final class StopwatchFragment extends DeskClockFragment {
}
}
+ @Override
+ public final int getFabTargetVisibility() {
+ return View.VISIBLE;
+ }
+
/**
* @param color the newly installed app window color
*/
diff --git a/src/com/android/deskclock/timer/TimerFragment.java b/src/com/android/deskclock/timer/TimerFragment.java
index 7779be656..0b2a49cec 100644
--- a/src/com/android/deskclock/timer/TimerFragment.java
+++ b/src/com/android/deskclock/timer/TimerFragment.java
@@ -236,6 +236,26 @@ public final class TimerFragment extends DeskClockFragment {
}
}
+ @Override
+ public int getFabTargetVisibility() {
+ if (mCurrentView == mTimersView) {
+ final Timer timer = getTimer();
+ if (timer == null) {
+ return INVISIBLE;
+ } else {
+ return VISIBLE;
+ }
+ } else if (mCurrentView == mCreateTimerView) {
+ if (mCreateTimerView.hasValidInput()) {
+ return VISIBLE;
+ } else {
+ return INVISIBLE;
+ }
+ }
+
+ return INVISIBLE;
+ }
+
private void updateFab(@NonNull ImageView fab) {
if (mCurrentView == mTimersView) {
final Timer timer = getTimer();
diff --git a/src/com/android/deskclock/uidata/TabModel.java b/src/com/android/deskclock/uidata/TabModel.java
index 5b878ed9d..9a1af3bd0 100644
--- a/src/com/android/deskclock/uidata/TabModel.java
+++ b/src/com/android/deskclock/uidata/TabModel.java
@@ -18,6 +18,7 @@ package com.android.deskclock.uidata;
import android.content.SharedPreferences;
import android.text.TextUtils;
+import androidx.annotation.Keep;
import java.util.ArrayList;
import java.util.Arrays;
@@ -88,6 +89,7 @@ final class TabModel {
* @param position the position of the tab in the user interface
* @return the tab at the given {@code ordinal}
*/
+ @Keep
Tab getTabAt(int position) {
final int ordinal;
if (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == LAYOUT_DIRECTION_RTL) {