diff --git a/app_pojavlauncher/src/main/java/android/support/design/widget/VerticalTabLayout.java b/app_pojavlauncher/src/main/java/android/support/design/widget/VerticalTabLayout.java deleted file mode 100644 index 24cbe89c9..000000000 --- a/app_pojavlauncher/src/main/java/android/support/design/widget/VerticalTabLayout.java +++ /dev/null @@ -1,2329 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.support.design.widget; - -import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; -import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_DRAGGING; -import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE; -import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_SETTLING; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.DataSetObserver; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.os.Build; -import androidx.annotation.ColorInt; -import androidx.annotation.DrawableRes; -import androidx.annotation.IntDef; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RestrictTo; -import androidx.annotation.StringRes; -import com.google.android.material.R; - -import androidx.core.util.Pools; -import androidx.core.view.GravityCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.PagerAdapter; -import androidx.core.view.PointerIconCompat; -import androidx.core.view.ViewCompat; -import androidx.viewpager.widget.ViewPager; -import androidx.core.widget.TextViewCompat; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.appcompat.widget.TooltipCompat; -import android.text.Layout; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.SoundEffectConstants; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.*; -import androidx.core.app.*; - -import com.google.android.material.animation.AnimationUtils; -import com.google.android.material.tabs.TabItem; -import com.google.android.material.tabs.TabLayout; - -/** - * VerticalTabLayout provides a vertical layout to display tabs. - * - *

Population of the tabs to display is - * done through {@link Tab} instances. You create tabs via {@link #newTab()}. From there you can - * change the tab's label or icon via {@link Tab#setText(int)} and {@link Tab#setIcon(int)} - * respectively. To display the tab, you need to add it to the layout via one of the - * {@link #addTab(Tab)} methods. For example: - *

- * VerticalTabLayout tabLayout = ...;
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
- * 
- * You should set a listener via {@link #setOnTabSelectedListener(OnTabSelectedListener)} to be - * notified when any tab's selection state has been changed. - * - *

You can also add items to VerticalTabLayout in your layout through the use of {@link TabItem}. - * An example usage is like so:

- * - *
- * <android.support.design.widget.TabLayout
- *         android:layout_height="wrap_content"
- *         android:layout_width="match_parent">
- *
- *     <android.support.design.widget.TabItem
- *             android:text="@string/tab_text"/>
- *
- *     <android.support.design.widget.TabItem
- *             android:icon="@drawable/ic_android"/>
- *
- * </android.support.design.widget.TabLayout>
- * 
- * - *

ViewPager integration

- *

- * If you're using a {@link ViewPager} together - * with this layout, you can call {@link #setupWithViewPager(ViewPager)} to link the two together. - * This layout will be automatically populated from the {@link PagerAdapter}'s page titles.

- * - *

- * This view also supports being used as part of a ViewPager's decor, and can be added - * directly to the ViewPager in a layout resource file like so:

- * - *
- * <android.support.v4.view.ViewPager
- *     android:layout_width="match_parent"
- *     android:layout_height="match_parent">
- *
- *     <android.support.design.widget.TabLayout
- *         android:layout_width="match_parent"
- *         android:layout_height="wrap_content"
- *         android:layout_gravity="top" />
- *
- * </android.support.v4.view.ViewPager>
- * 
- * - * @see Tabs - * - * @attr ref android.support.design.R.styleable#TabLayout_tabPadding - * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingStart - * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingTop - * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingEnd - * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingBottom - * @attr ref android.support.design.R.styleable#TabLayout_tabContentStart - * @attr ref android.support.design.R.styleable#TabLayout_tabBackground - * @attr ref android.support.design.R.styleable#TabLayout_tabMinWidth - * @attr ref android.support.design.R.styleable#TabLayout_tabMaxWidth - * @attr ref android.support.design.R.styleable#TabLayout_tabTextAppearance - */ -@ViewPager.DecorView -public class VerticalTabLayout extends LinearLayout { - - private static final int DEFAULT_HEIGHT_WITH_TEXT_ICON = 72; // dps - static final int DEFAULT_GAP_TEXT_ICON = 8; // dps - private static final int INVALID_WIDTH = -1; - private static final int DEFAULT_HEIGHT = 48; // dps - private static final int TAB_MIN_WIDTH_MARGIN = 56; //dps - static final int FIXED_WRAP_GUTTER_MIN = 16; //dps - static final int MOTION_NON_ADJACENT_OFFSET = 24; - - private static final int ANIMATION_DURATION = 300; - - private static final Pools.Pool sTabPool = new Pools.SynchronizedPool<>(16); - - /** - * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab - * labels and a larger number of tabs. They are best used for browsing contexts in touch - * interfaces when users don’t need to directly compare the tab labels. - * - * @see #setTabMode(int) - * @see #getTabMode() - */ - public static final int MODE_SCROLLABLE = 0; - - /** - * Fixed tabs display all tabs concurrently and are best used with content that benefits from - * quick pivots between tabs. The maximum number of tabs is limited by the view’s width. - * Fixed tabs have equal width, based on the widest tab label. - * - * @see #setTabMode(int) - * @see #getTabMode() - */ - public static final int MODE_FIXED = 1; - - /** - * @hide - */ - @RestrictTo(LIBRARY_GROUP) - @IntDef(value = {MODE_SCROLLABLE, MODE_FIXED}) - @Retention(RetentionPolicy.SOURCE) - public @interface Mode {} - - /** - * Gravity used to fill the {@link TabLayout} as much as possible. This option only takes effect - * when used with {@link #MODE_FIXED}. - * - * @see #setTabGravity(int) - * @see #getTabGravity() - */ - public static final int GRAVITY_FILL = 0; - - /** - * Gravity used to lay out the tabs in the center of the {@link TabLayout}. - * - * @see #setTabGravity(int) - * @see #getTabGravity() - */ - public static final int GRAVITY_CENTER = 1; - - /** - * @hide - */ - @RestrictTo(LIBRARY_GROUP) - @IntDef(flag = true, value = {GRAVITY_FILL, GRAVITY_CENTER}) - @Retention(RetentionPolicy.SOURCE) - public @interface TabGravity {} - - /** - * Callback interface invoked when a tab's selection state changes. - */ - public interface OnTabSelectedListener { - - /** - * Called when a tab enters the selected state. - * - * @param tab The tab that was selected - */ - public void onTabSelected(Tab tab); - - /** - * Called when a tab exits the selected state. - * - * @param tab The tab that was unselected - */ - public void onTabUnselected(Tab tab); - - /** - * Called when a tab that is already selected is chosen again by the user. Some applications - * may use this action to return to the top level of a category. - * - * @param tab The tab that was reselected. - */ - public void onTabReselected(Tab tab); - } - - private final ArrayList mTabs = new ArrayList<>(); - private Tab mSelectedTab; - - private final ScrollView mTopScrollView; - private final SlidingTabStrip mTabStrip; - - int mTabPaddingStart; - int mTabPaddingTop; - int mTabPaddingEnd; - int mTabPaddingBottom; - - int mTabTextAppearance; - ColorStateList mTabTextColors; - float mTabTextSize; - float mTabTextMultiLineSize; - - final int mTabBackgroundResId; - - int mTabMaxWidth = Integer.MAX_VALUE; - private final int mRequestedTabMinWidth; - private final int mRequestedTabMaxWidth; - private final int mScrollableTabMinWidth; - - private int mContentInsetStart; - - int mTabGravity; - int mMode; - - private OnTabSelectedListener mSelectedListener; - private final ArrayList mSelectedListeners = new ArrayList<>(); - private OnTabSelectedListener mCurrentVpSelectedListener; - - private ValueAnimator mScrollAnimator; - - ViewPager mViewPager; - private ViewPagerAdapter mPagerAdapter; - private DataSetObserver mPagerAdapterObserver; - private VerticalTabLayoutOnPageChangeListener mPageChangeListener; - private AdapterChangeListener mAdapterChangeListener; - private boolean mSetupViewPagerImplicitly; - - // Pool we use as a simple RecyclerBin - private final Pools.Pool mTabViewPool = new Pools.SimplePool<>(12); - - public VerticalTabLayout(Context context) { - this(context, null); - } - - public VerticalTabLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public VerticalTabLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - //ndroidx.appcompat.widget.ThemeUtils.checkAppCompatTheme(context); - - setOrientation(VERTICAL); - - // Disable the Scroll Bar - // setVerticalScrollBarEnabled(false); - - LayoutParams scrollViewParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - scrollViewParams.weight = 1f; - mTopScrollView = new ScrollView(context); - mTopScrollView.setLayoutParams(scrollViewParams); - - // Add the TabStrip - mTabStrip = new SlidingTabStrip(context); - mTopScrollView.addView(mTabStrip, 0, new ScrollView.LayoutParams( - LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); - super.addView(mTopScrollView); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout, - defStyleAttr, R.style.Widget_Design_TabLayout); - - mTabStrip.setSelectedIndicatorHeight( - a.getDimensionPixelSize(R.styleable.TabLayout_tabIndicatorHeight, 0)); - mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0)); - - mTabPaddingStart = mTabPaddingTop = mTabPaddingEnd = mTabPaddingBottom = a - .getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0); - mTabPaddingStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart, - mTabPaddingStart); - mTabPaddingTop = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingTop, - mTabPaddingTop); - mTabPaddingEnd = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd, - mTabPaddingEnd); - mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom, - mTabPaddingBottom); - - mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance, - R.style.TextAppearance_Design_Tab); - - // Text colors/sizes come from the text appearance first - final TypedArray ta = context.obtainStyledAttributes(mTabTextAppearance, - androidx.appcompat.R.styleable.TextAppearance); - try { - mTabTextSize = ta.getDimensionPixelSize( - androidx.appcompat.R.styleable.TextAppearance_android_textSize, 0); - mTabTextColors = ta.getColorStateList( - androidx.appcompat.R.styleable.TextAppearance_android_textColor); - } finally { - ta.recycle(); - } - - if (a.hasValue(R.styleable.TabLayout_tabTextColor)) { - // If we have an explicit text color set, use it instead - mTabTextColors = a.getColorStateList(R.styleable.TabLayout_tabTextColor); - } - - if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) { - // We have an explicit selected text color set, so we need to make merge it with the - // current colors. This is exposed so that developers can use theme attributes to set - // this (theme attrs in ColorStateLists are Lollipop+) - final int selected = a.getColor(R.styleable.TabLayout_tabSelectedTextColor, 0); - mTabTextColors = createColorStateList(mTabTextColors.getDefaultColor(), selected); - } - - mRequestedTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth, - INVALID_WIDTH); - mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth, - INVALID_WIDTH); - mTabBackgroundResId = a.getResourceId(R.styleable.TabLayout_tabBackground, 0); - mContentInsetStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabContentStart, 0); - mMode = a.getInt(R.styleable.TabLayout_tabMode, MODE_FIXED); - mTabGravity = a.getInt(R.styleable.TabLayout_tabGravity, GRAVITY_FILL); - a.recycle(); - - // TODO add attr for these - final Resources res = getResources(); - mTabTextMultiLineSize = res.getDimensionPixelSize(R.dimen.design_tab_text_size_2line); - mScrollableTabMinWidth = res.getDimensionPixelSize(R.dimen.design_tab_scrollable_min_width); - - // Now apply the tab mode and gravity - applyModeAndGravity(); - } - - /** - * Sets the tab indicator's color for the currently selected tab. - * - * @param color color to use for the indicator - * - * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorColor - */ - public void setSelectedTabIndicatorColor(@ColorInt int color) { - mTabStrip.setSelectedIndicatorColor(color); - } - - /** - * Sets the tab indicator's height for the currently selected tab. - * - * @param height height to use for the indicator in pixels - * - * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorHeight - */ - public void setSelectedTabIndicatorHeight(int height) { - mTabStrip.setSelectedIndicatorHeight(height); - } - - /** - * Set the scroll position of the tabs. This is useful for when the tabs are being displayed as - * part of a scrolling container such as {@link ViewPager}. - *

- * Calling this method does not update the selected tab, it is only used for drawing purposes. - * - * @param position current scroll position - * @param positionOffset Value from [0, 1) indicating the offset from {@code position}. - * @param updateSelectedText Whether to update the text's selected state. - */ - public void setScrollPosition(int position, float positionOffset, boolean updateSelectedText) { - setScrollPosition(position, positionOffset, updateSelectedText, true); - } - - void setScrollPosition(int position, float positionOffset, boolean updateSelectedText, - boolean updateIndicatorPosition) { - final int roundedPosition = Math.round(position + positionOffset); - if (roundedPosition < 0 || roundedPosition >= mTabStrip.getChildCount()) { - return; - } - - // Set the indicator position, if enabled - if (updateIndicatorPosition) { - mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset); - } - - // Now update the scroll position, canceling any running animation - if (mScrollAnimator != null && mScrollAnimator.isRunning()) { - mScrollAnimator.cancel(); - } - mTopScrollView.scrollTo(calculateScrollXForTab(position, positionOffset), 0); - - // Update the 'selected state' view as we scroll, if enabled - if (updateSelectedText) { - setSelectedTabView(roundedPosition); - } - } - - private float getScrollPosition() { - return mTabStrip.getIndicatorPosition(); - } - - public void setLastTabAsBottom() { - final int position = mTabs.size() - 1; - final int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : 0; - final TabView view = (TabView) mTabStrip.getChildAt(position); - view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - mTabStrip.removeViewAt(position); - requestLayout(); - - mTabs.remove(position); - super.addView(view); - - final int newTabCount = mTabs.size(); - for (int i = position; i < newTabCount; i++) { - mTabs.get(i).setPosition(i); - } - - if (selectedTabPosition == position) { - selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1))); - } - } - - /** - * Add a tab to this layout. The tab will be added at the end of the list. - * If this is the first tab to be added it will become the selected tab. - * - * @param tab Tab to add - */ - public void addTab(@NonNull Tab tab) { - addTab(tab, mTabs.isEmpty()); - } - - /** - * Add a tab to this layout. The tab will be inserted at position. - * If this is the first tab to be added it will become the selected tab. - * - * @param tab The tab to add - * @param position The new position of the tab - */ - public void addTab(@NonNull Tab tab, int position) { - addTab(tab, position, mTabs.isEmpty()); - } - - /** - * Add a tab to this layout. The tab will be added at the end of the list. - * - * @param tab Tab to add - * @param setSelected True if the added tab should become the selected tab. - */ - public void addTab(@NonNull Tab tab, boolean setSelected) { - addTab(tab, mTabs.size(), setSelected); - } - - /** - * Add a tab to this layout. The tab will be inserted at position. - * - * @param tab The tab to add - * @param position The new position of the tab - * @param setSelected True if the added tab should become the selected tab. - */ - public void addTab(@NonNull Tab tab, int position, boolean setSelected) { - if (tab.mParent != this) { - throw new IllegalArgumentException("Tab belongs to a different TabLayout."); - } - configureTab(tab, position); - addTabView(tab); - - if (setSelected) { - tab.select(); - } - } - - private void addTabFromItemView(@NonNull TabItem item) { - - final Tab tab = newTab(); - if (item.text != null) { - tab.setText(item.text); - } - if (item.icon != null) { - tab.setIcon(item.icon); - } - if (item.customLayout != 0) { - tab.setCustomView(item.customLayout); - } - if (!TextUtils.isEmpty(item.getContentDescription())) { - tab.setContentDescription(item.getContentDescription()); - } - addTab(tab); - } - - /** - * @deprecated Use {@link #addOnTabSelectedListener(OnTabSelectedListener)} and - * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}. - */ - @Deprecated - public void setOnTabSelectedListener(@Nullable OnTabSelectedListener listener) { - // The logic in this method emulates what we had before support for multiple - // registered listeners. - if (mSelectedListener != null) { - removeOnTabSelectedListener(mSelectedListener); - } - // Update the deprecated field so that we can remove the passed listener the next - // time we're called - mSelectedListener = listener; - if (listener != null) { - addOnTabSelectedListener(listener); - } - } - - /** - * Add a {@link TabLayout.OnTabSelectedListener} that will be invoked when tab selection - * changes. - * - *

Components that add a listener should take care to remove it when finished via - * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}.

- * - * @param listener listener to add - */ - public void addOnTabSelectedListener(@NonNull OnTabSelectedListener listener) { - if (!mSelectedListeners.contains(listener)) { - mSelectedListeners.add(listener); - } - } - - /** - * Remove the given {@link TabLayout.OnTabSelectedListener} that was previously added via - * {@link #addOnTabSelectedListener(OnTabSelectedListener)}. - * - * @param listener listener to remove - */ - public void removeOnTabSelectedListener(@NonNull OnTabSelectedListener listener) { - mSelectedListeners.remove(listener); - } - - /** - * Remove all previously added {@link TabLayout.OnTabSelectedListener}s. - */ - public void clearOnTabSelectedListeners() { - mSelectedListeners.clear(); - } - - /** - * Create and return a new {@link Tab}. You need to manually add this using - * {@link #addTab(Tab)} or a related method. - * - * @return A new Tab - * @see #addTab(Tab) - */ - @NonNull - public Tab newTab() { - Tab tab = sTabPool.acquire(); - if (tab == null) { - tab = new Tab(); - } - tab.mParent = this; - tab.mView = createTabView(tab); - return tab; - } - - /** - * Returns the number of tabs currently registered with the action bar. - * - * @return Tab count - */ - public int getTabCount() { - return mTabs.size(); - } - - /** - * Returns the tab at the specified index. - */ - @Nullable - public Tab getTabAt(int index) { - return (index < 0 || index >= getTabCount()) ? null : mTabs.get(index); - } - - /** - * Returns the position of the current selected tab. - * - * @return selected tab position, or {@code -1} if there isn't a selected tab. - */ - public int getSelectedTabPosition() { - return mSelectedTab != null ? mSelectedTab.getPosition() : -1; - } - - /** - * Remove a tab from the layout. If the removed tab was selected it will be deselected - * and another tab will be selected if present. - * - * @param tab The tab to remove - */ - public void removeTab(Tab tab) { - if (tab.mParent != this) { - throw new IllegalArgumentException("Tab does not belong to this TabLayout."); - } - - removeTabAt(tab.getPosition()); - } - - /** - * Remove a tab from the layout. If the removed tab was selected it will be deselected - * and another tab will be selected if present. - * - * @param position Position of the tab to remove - */ - public void removeTabAt(int position) { - final int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : 0; - removeTabViewAt(position); - - final Tab removedTab = mTabs.remove(position); - if (removedTab != null) { - removedTab.reset(); - sTabPool.release(removedTab); - } - - final int newTabCount = mTabs.size(); - for (int i = position; i < newTabCount; i++) { - mTabs.get(i).setPosition(i); - } - - if (selectedTabPosition == position) { - selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1))); - } - } - - /** - * Remove all tabs from the action bar and deselect the current tab. - */ - public void removeAllTabs() { - // Remove all the views - for (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) { - removeTabViewAt(i); - } - - for (final Iterator i = mTabs.iterator(); i.hasNext();) { - final Tab tab = i.next(); - i.remove(); - tab.reset(); - sTabPool.release(tab); - } - - mSelectedTab = null; - } - - /** - * Set the behavior mode for the Tabs in this layout. The valid input options are: - *
    - *
  • {@link #MODE_FIXED}: Fixed tabs display all tabs concurrently and are best used - * with content that benefits from quick pivots between tabs.
  • - *
  • {@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment, - * and can contain longer tab labels and a larger number of tabs. They are best used for - * browsing contexts in touch interfaces when users don’t need to directly compare the tab - * labels. This mode is commonly used with a {@link ViewPager}.
  • - *
- * - * @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}. - * - * @attr ref android.support.design.R.styleable#TabLayout_tabMode - */ - public void setTabMode(@Mode int mode) { - if (mode != mMode) { - mMode = mode; - applyModeAndGravity(); - } - } - - /** - * Returns the current mode used by this {@link TabLayout}. - * - * @see #setTabMode(int) - */ - @Mode - public int getTabMode() { - return mMode; - } - - /** - * Set the gravity to use when laying out the tabs. - * - * @param gravity one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}. - * - * @attr ref android.support.design.R.styleable#TabLayout_tabGravity - */ - public void setTabGravity(@TabGravity int gravity) { - if (mTabGravity != gravity) { - mTabGravity = gravity; - applyModeAndGravity(); - } - } - - /** - * The current gravity used for laying out tabs. - * - * @return one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}. - */ - @TabGravity - public int getTabGravity() { - return mTabGravity; - } - - /** - * Sets the text colors for the different states (normal, selected) used for the tabs. - * - * @see #getTabTextColors() - */ - public void setTabTextColors(@Nullable ColorStateList textColor) { - if (mTabTextColors != textColor) { - mTabTextColors = textColor; - updateAllTabs(); - } - } - - /** - * Gets the text colors for the different states (normal, selected) used for the tabs. - */ - @Nullable - public ColorStateList getTabTextColors() { - return mTabTextColors; - } - - /** - * Sets the text colors for the different states (normal, selected) used for the tabs. - * - * @attr ref android.support.design.R.styleable#TabLayout_tabTextColor - * @attr ref android.support.design.R.styleable#TabLayout_tabSelectedTextColor - */ - public void setTabTextColors(int normalColor, int selectedColor) { - setTabTextColors(createColorStateList(normalColor, selectedColor)); - } - - /** - * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}. - * - *

This is the same as calling {@link #setupWithViewPager(ViewPager, boolean)} with - * auto-refresh enabled.

- * - * @param viewPager the ViewPager to link to, or {@code null} to clear any previous link - */ - public void setupWithViewPager(@Nullable ViewPager viewPager) { - setupWithViewPager(viewPager, true); - } - - /** - * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}. - * - *

This method will link the given ViewPager and this VerticalTabLayout together so that - * changes in one are automatically reflected in the other. This includes scroll state changes - * and clicks. The tabs displayed in this layout will be populated - * from the ViewPager adapter's page titles.

- * - *

If {@code autoRefresh} is {@code true}, any changes in the {@link PagerAdapter} will - * trigger this layout to re-populate itself from the adapter's titles.

- * - *

If the given ViewPager is non-null, it needs to already have a - * {@link PagerAdapter} set.

- * - * @param viewPager the ViewPager to link to, or {@code null} to clear any previous link - * @param autoRefresh whether this layout should refresh its contents if the given ViewPager's - * content changes - */ - public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh) { - setupWithViewPager(viewPager, autoRefresh, false); - } - - private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh, - boolean implicitSetup) { - if (mViewPager != null) { - // If we've already been setup with a ViewPager, remove us from it - if (mPageChangeListener != null) { - mViewPager.removeOnPageChangeListener(mPageChangeListener); - } - if (mAdapterChangeListener != null) { - mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener); - } - } - - if (mCurrentVpSelectedListener != null) { - // If we already have a tab selected listener for the ViewPager, remove it - removeOnTabSelectedListener(mCurrentVpSelectedListener); - mCurrentVpSelectedListener = null; - } - - if (viewPager != null) { - mViewPager = viewPager; - - // Add our custom OnPageChangeListener to the ViewPager - if (mPageChangeListener == null) { - mPageChangeListener = new VerticalTabLayoutOnPageChangeListener(this); - } - mPageChangeListener.reset(); - viewPager.addOnPageChangeListener(mPageChangeListener); - - // Now we'll add a tab selected listener to set ViewPager's current item - mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager); - addOnTabSelectedListener(mCurrentVpSelectedListener); - - final ViewPagerAdapter adapter = (VerticalTabLayout.ViewPagerAdapter) viewPager.getAdapter(); - if (adapter != null) { - // Now we'll populate ourselves from the pager adapter, adding an observer if - // autoRefresh is enabled - setPagerAdapter(adapter, autoRefresh); - } - - // Add a listener so that we're notified of any adapter changes - if (mAdapterChangeListener == null) { - mAdapterChangeListener = new AdapterChangeListener(); - } - mAdapterChangeListener.setAutoRefresh(autoRefresh); - viewPager.addOnAdapterChangeListener(mAdapterChangeListener); - - // Now update the scroll position to match the ViewPager's current item - setScrollPosition(viewPager.getCurrentItem(), 0f, true); - } else { - // We've been given a null ViewPager so we need to clear out the internal state, - // listeners and observers - mViewPager = null; - setPagerAdapter(null, false); - } - - mSetupViewPagerImplicitly = implicitSetup; - } - - /** - * @deprecated Use {@link #setupWithViewPager(ViewPager)} to link a VerticalTabLayout with a ViewPager - * together. When that method is used, the VerticalTabLayout will be automatically updated - * when the {@link PagerAdapter} is changed. - */ - @Deprecated - public void setTabsFromPagerAdapter(@Nullable final ViewPagerAdapter adapter) { - setPagerAdapter(adapter, false); - } - - @Override - public boolean shouldDelayChildPressedState() { - // Only delay the pressed state if the tabs can scroll - return getTabScrollRange() > 0; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mViewPager == null) { - // If we don't have a ViewPager already, check if our parent is a ViewPager to - // setup with it automatically - final ViewParent vp = getParent(); - if (vp instanceof ViewPager) { - // If we have a ViewPager parent and we've been added as part of its decor, let's - // assume that we should automatically setup to display any titles - setupWithViewPager((ViewPager) vp, true, true); - } - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - if (mSetupViewPagerImplicitly) { - // If we've been setup with a ViewPager implicitly, let's clear out any listeners, etc - setupWithViewPager(null); - mSetupViewPagerImplicitly = false; - } - } - - private int getTabScrollRange() { - return Math.max(0, mTabStrip.getWidth() - mTopScrollView.getWidth() - mTopScrollView.getPaddingLeft() - - mTopScrollView.getPaddingRight()); - } - - void setPagerAdapter(@Nullable final ViewPagerAdapter adapter, final boolean addObserver) { - if (mPagerAdapter != null && mPagerAdapterObserver != null) { - // If we already have a PagerAdapter, unregister our observer - mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver); - } - - mPagerAdapter = adapter; - - if (addObserver && adapter != null) { - // Register our observer on the new adapter - if (mPagerAdapterObserver == null) { - mPagerAdapterObserver = new PagerAdapterObserver(); - } - adapter.registerDataSetObserver(mPagerAdapterObserver); - } - - // Finally make sure we reflect the new adapter - populateFromPagerAdapter(); - } - - void populateFromPagerAdapter() { - removeAllTabs(); - - if (mPagerAdapter != null) { - final int adapterCount = mPagerAdapter.getCount(); - for (int i = 0; i < adapterCount; i++) { - addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false); - if (mPagerAdapter.getIcon(i) != 0) { - getTabAt(i).setIcon(mPagerAdapter.getIcon(i)); - } - } - - // Make sure we reflect the currently set ViewPager item - if (mViewPager != null && adapterCount > 0) { - final int curItem = mViewPager.getCurrentItem(); - if (curItem != getSelectedTabPosition() && curItem < getTabCount()) { - selectTab(getTabAt(curItem)); - } - } - } - } - - private void updateAllTabs() { - for (int i = 0, z = mTabs.size(); i < z; i++) { - mTabs.get(i).updateView(); - } - } - - private TabView createTabView(@NonNull final Tab tab) { - TabView tabView = mTabViewPool != null ? mTabViewPool.acquire() : null; - if (tabView == null) { - tabView = new TabView(getContext()); - } - tabView.setTab(tab); - tabView.setFocusable(true); - tabView.setMinimumWidth(getTabMinWidth()); - return tabView; - } - - private void configureTab(Tab tab, int position) { - tab.setPosition(position); - mTabs.add(position, tab); - - final int count = mTabs.size(); - for (int i = position + 1; i < count; i++) { - mTabs.get(i).setPosition(i); - } - } - - private void addTabView(Tab tab) { - final TabView tabView = tab.mView; - mTabStrip.addView(tabView, tab.getPosition(), createLayoutParamsForTabs()); - } - - // @Override - public void addViewItem(View child) { - addViewInternal(child); - } - - // @Override - public void addViewItem(View child, int index) { - addViewInternal(child); - } - - // @Override - public void addViewItem(View child, ViewGroup.LayoutParams params) { - addViewInternal(child); - } - - // @Override - public void addViewItem(View child, int index, ViewGroup.LayoutParams params) { - addViewInternal(child); - } - - private void addViewInternal(final View child) { - if (child instanceof TabItem) { - addTabFromItemView((TabItem) child); - } else { - throw new IllegalArgumentException("Only TabItem instances can be added to TabLayout"); - } - } - - private LinearLayout.LayoutParams createLayoutParamsForTabs() { - final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - updateTabViewLayoutParams(lp); - return lp; - } - - private void updateTabViewLayoutParams(LinearLayout.LayoutParams lp) { - if (mMode == MODE_FIXED && mTabGravity == GRAVITY_FILL) { - lp.width = 0; - lp.weight = 1; - } else { - lp.width = LinearLayout.LayoutParams.WRAP_CONTENT; - lp.weight = 0; - } - } - - int dpToPx(int dps) { - return Math.round(getResources().getDisplayMetrics().density * dps); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // If we have a MeasureSpec which allows us to decide our height, try and use the default - // height - final int idealHeight = dpToPx(getDefaultHeight()) + mTopScrollView.getPaddingTop() + mTopScrollView.getPaddingBottom(); - - switch (MeasureSpec.getMode(heightMeasureSpec)) { - case MeasureSpec.AT_MOST: - heightMeasureSpec = MeasureSpec.makeMeasureSpec( - Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec)), - MeasureSpec.EXACTLY); - break; - case 0 /* MeasureSpec.UNSPECIFIED */: - heightMeasureSpec = MeasureSpec.makeMeasureSpec(idealHeight, MeasureSpec.EXACTLY); - break; - } - - final int specWidth = MeasureSpec.getSize(widthMeasureSpec); - if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) { - // If we don't have an unspecified width spec, use the given size to calculate - // the max tab width - mTabMaxWidth = mRequestedTabMaxWidth > 0 - ? mRequestedTabMaxWidth - : specWidth - dpToPx(TAB_MIN_WIDTH_MARGIN); - } - - // Now super measure itself using the (possibly) modified height spec - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (getChildCount() == 1) { - // If we're in fixed mode then we need to make the tab strip is the same width as us - // so we don't scroll - final View child = getChildAt(0); - boolean remeasure = false; - - switch (mMode) { - case MODE_SCROLLABLE: - // We only need to resize the child if it's smaller than us. This is similar - // to fillViewport - remeasure = child.getMeasuredWidth() < mTopScrollView.getMeasuredWidth(); - break; - case MODE_FIXED: - // Resize the child so that it doesn't scroll - remeasure = child.getMeasuredWidth() != mTopScrollView.getMeasuredWidth(); - break; - } - - if (remeasure) { - // Re-measure the child with a widthSpec set to be exactly our measure width - int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, mTopScrollView.getPaddingTop() - + mTopScrollView.getPaddingBottom(), child.getLayoutParams().height); - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - mTopScrollView.getMeasuredWidth(), MeasureSpec.EXACTLY); - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - } - } - - private void removeTabViewAt(int position) { - final TabView view = (TabView) mTabStrip.getChildAt(position); - mTabStrip.removeViewAt(position); - if (view != null) { - view.reset(); - mTabViewPool.release(view); - } - requestLayout(); - } - - private void animateToTab(int newPosition) { - if (newPosition == Tab.INVALID_POSITION) { - return; - } - - if (getWindowToken() == null || !ViewCompat.isLaidOut(this) - || mTabStrip.childrenNeedLayout()) { - // If we don't have a window token, or we haven't been laid out yet just draw the new - // position now - setScrollPosition(newPosition, 0f, true); - return; - } - - final int startScrollX = mTopScrollView.getScrollX(); - final int targetScrollX = calculateScrollXForTab(newPosition, 0); - - if (startScrollX != targetScrollX) { - ensureScrollAnimator(); - - mScrollAnimator.setIntValues(startScrollX, targetScrollX); - mScrollAnimator.start(); - } - - // Now animate the indicator - mTabStrip.animateIndicatorToPosition(newPosition, ANIMATION_DURATION); - } - - private void ensureScrollAnimator() { - if (mScrollAnimator == null) { - mScrollAnimator = new ValueAnimator(); - mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); - mScrollAnimator.setDuration(ANIMATION_DURATION); - mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animator) { - scrollTo((int) animator.getAnimatedValue(), 0); - } - }); - } - } - - void setScrollAnimatorListener(Animator.AnimatorListener listener) { - ensureScrollAnimator(); - mScrollAnimator.addListener(listener); - } - - private void setSelectedTabView(int position) { - final int tabCount = mTabStrip.getChildCount(); - if (position < tabCount) { - for (int i = 0; i < tabCount; i++) { - final View child = mTabStrip.getChildAt(i); - child.setSelected(i == position); - } - } - } - - void selectTab(Tab tab) { - selectTab(tab, true); - } - - void selectTab(final Tab tab, boolean updateIndicator) { - final Tab currentTab = mSelectedTab; - - if (currentTab == tab) { - if (currentTab != null) { - dispatchTabReselected(tab); - animateToTab(tab.getPosition()); - } - } else { - final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION; - if (updateIndicator) { - if ((currentTab == null || currentTab.getPosition() == Tab.INVALID_POSITION) - && newPosition != Tab.INVALID_POSITION) { - // If we don't currently have a tab, just draw the indicator - setScrollPosition(newPosition, 0f, true); - } else { - animateToTab(newPosition); - } - if (newPosition != Tab.INVALID_POSITION) { - setSelectedTabView(newPosition); - } - } - if (currentTab != null) { - dispatchTabUnselected(currentTab); - } - mSelectedTab = tab; - if (tab != null) { - dispatchTabSelected(tab); - } - } - } - - private void dispatchTabSelected(@NonNull final Tab tab) { - for (int i = mSelectedListeners.size() - 1; i >= 0; i--) { - mSelectedListeners.get(i).onTabSelected(tab); - } - } - - private void dispatchTabUnselected(@NonNull final Tab tab) { - for (int i = mSelectedListeners.size() - 1; i >= 0; i--) { - mSelectedListeners.get(i).onTabUnselected(tab); - } - } - - private void dispatchTabReselected(@NonNull final Tab tab) { - for (int i = mSelectedListeners.size() - 1; i >= 0; i--) { - mSelectedListeners.get(i).onTabReselected(tab); - } - } - - private int calculateScrollXForTab(int position, float positionOffset) { - if (mMode == MODE_SCROLLABLE) { - final View selectedChild = mTabStrip.getChildAt(position); - final View nextChild = position + 1 < mTabStrip.getChildCount() - ? mTabStrip.getChildAt(position + 1) - : null; - final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0; - final int nextWidth = nextChild != null ? nextChild.getWidth() : 0; - - // base scroll amount: places center of tab in center of parent - int scrollBase = selectedChild.getLeft() + (selectedWidth / 2) - (mTopScrollView.getWidth() / 2); - // offset amount: fraction of the distance between centers of tabs - int scrollOffset = (int) ((selectedWidth + nextWidth) * 0.5f * positionOffset); - - return (ViewCompat.getLayoutDirection(mTopScrollView) == ViewCompat.LAYOUT_DIRECTION_LTR) - ? scrollBase + scrollOffset - : scrollBase - scrollOffset; - } - return 0; - } - - private void applyModeAndGravity() { - int paddingStart = 0; - if (mMode == MODE_SCROLLABLE) { - // If we're scrollable, or fixed at start, inset using padding - paddingStart = Math.max(0, mContentInsetStart - mTabPaddingStart); - } - ViewCompat.setPaddingRelative(mTabStrip, paddingStart, 0, 0, 0); - - switch (mMode) { - case MODE_FIXED: - mTabStrip.setGravity(Gravity.CENTER_VERTICAL); - break; - case MODE_SCROLLABLE: - mTabStrip.setGravity(GravityCompat.START); - break; - } - - updateTabViews(true); - } - - void updateTabViews(final boolean requestLayout) { - for (int i = 0; i < mTabStrip.getChildCount(); i++) { - View child = mTabStrip.getChildAt(i); - child.setMinimumWidth(getTabMinWidth()); - updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams()); - if (requestLayout) { - child.requestLayout(); - } - } - } - - /** - * A tab in this layout. Instances can be created via {@link #newTab()}. - */ - public static final class Tab { - - /** - * An invalid position for a tab. - * - * @see #getPosition() - */ - public static final int INVALID_POSITION = -1; - - private Object mTag; - private Drawable mIcon; - private CharSequence mText; - private CharSequence mContentDesc; - private int mPosition = INVALID_POSITION; - private View mCustomView; - - VerticalTabLayout mParent; - TabView mView; - - Tab() { - // Private constructor - } - - /** - * @return This Tab's tag object. - */ - @Nullable - public Object getTag() { - return mTag; - } - - /** - * Give this Tab an arbitrary object to hold for later use. - * - * @param tag Object to store - * @return The current instance for call chaining - */ - @NonNull - public Tab setTag(@Nullable Object tag) { - mTag = tag; - return this; - } - - - /** - * Returns the custom view used for this tab. - * - * @see #setCustomView(View) - * @see #setCustomView(int) - */ - @Nullable - public View getCustomView() { - return mCustomView; - } - - /** - * Set a custom view to be used for this tab. - *

- * If the provided view contains a {@link TextView} with an ID of - * {@link android.R.id#text1} then that will be updated with the value given - * to {@link #setText(CharSequence)}. Similarly, if this layout contains an - * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with - * the value given to {@link #setIcon(Drawable)}. - *

- * - * @param view Custom view to be used as a tab. - * @return The current instance for call chaining - */ - @NonNull - public Tab setCustomView(@Nullable View view) { - mCustomView = view; - updateView(); - return this; - } - - /** - * Set a custom view to be used for this tab. - *

- * If the inflated layout contains a {@link TextView} with an ID of - * {@link android.R.id#text1} then that will be updated with the value given - * to {@link #setText(CharSequence)}. Similarly, if this layout contains an - * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with - * the value given to {@link #setIcon(Drawable)}. - *

- * - * @param resId A layout resource to inflate and use as a custom tab view - * @return The current instance for call chaining - */ - @NonNull - public Tab setCustomView(@LayoutRes int resId) { - final LayoutInflater inflater = LayoutInflater.from(mView.getContext()); - return setCustomView(inflater.inflate(resId, mView, false)); - } - - /** - * Return the icon associated with this tab. - * - * @return The tab's icon - */ - @Nullable - public Drawable getIcon() { - return mIcon; - } - - /** - * Return the current position of this tab in the action bar. - * - * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in - * the action bar. - */ - public int getPosition() { - return mPosition; - } - - void setPosition(int position) { - mPosition = position; - } - - /** - * Return the text of this tab. - * - * @return The tab's text - */ - @Nullable - public CharSequence getText() { - return mText; - } - - /** - * Set the icon displayed on this tab. - * - * @param icon The drawable to use as an icon - * @return The current instance for call chaining - */ - @NonNull - public Tab setIcon(@Nullable Drawable icon) { - mIcon = icon; - updateView(); - return this; - } - - /** - * Set the icon displayed on this tab. - * - * @param resId A resource ID referring to the icon that should be displayed - * @return The current instance for call chaining - */ - @NonNull - public Tab setIcon(@DrawableRes int resId) { - if (mParent == null) { - throw new IllegalArgumentException("Tab not attached to a TabLayout"); - } - return setIcon(resId == 0 ? null : AppCompatResources.getDrawable(mParent.getContext(), resId)); - } - - /** - * Set the text displayed on this tab. Text may be truncated if there is not room to display - * the entire string. - * - * @param text The text to display - * @return The current instance for call chaining - */ - @NonNull - public Tab setText(@Nullable CharSequence text) { - mText = text; - updateView(); - return this; - } - - /** - * Set the text displayed on this tab. Text may be truncated if there is not room to display - * the entire string. - * - * @param resId A resource ID referring to the text that should be displayed - * @return The current instance for call chaining - */ - @NonNull - public Tab setText(@StringRes int resId) { - if (mParent == null) { - throw new IllegalArgumentException("Tab not attached to a TabLayout"); - } - return setText(mParent.getResources().getText(resId)); - } - - /** - * Select this tab. Only valid if the tab has been added to the action bar. - */ - public void select() { - if (mParent == null) { - throw new IllegalArgumentException("Tab not attached to a TabLayout"); - } - mParent.selectTab(this); - } - - /** - * Returns true if this tab is currently selected. - */ - public boolean isSelected() { - if (mParent == null) { - throw new IllegalArgumentException("Tab not attached to a TabLayout"); - } - return mParent.getSelectedTabPosition() == mPosition; - } - - /** - * Set a description of this tab's content for use in accessibility support. If no content - * description is provided the title will be used. - * - * @param resId A resource ID referring to the description text - * @return The current instance for call chaining - * @see #setContentDescription(CharSequence) - * @see #getContentDescription() - */ - @NonNull - public Tab setContentDescription(@StringRes int resId) { - if (mParent == null) { - throw new IllegalArgumentException("Tab not attached to a TabLayout"); - } - return setContentDescription(mParent.getResources().getText(resId)); - } - - /** - * Set a description of this tab's content for use in accessibility support. If no content - * description is provided the title will be used. - * - * @param contentDesc Description of this tab's content - * @return The current instance for call chaining - * @see #setContentDescription(int) - * @see #getContentDescription() - */ - @NonNull - public Tab setContentDescription(@Nullable CharSequence contentDesc) { - mContentDesc = contentDesc; - updateView(); - return this; - } - - /** - * Gets a brief description of this tab's content for use in accessibility support. - * - * @return Description of this tab's content - * @see #setContentDescription(CharSequence) - * @see #setContentDescription(int) - */ - @Nullable - public CharSequence getContentDescription() { - return mContentDesc; - } - - void updateView() { - if (mView != null) { - mView.update(); - } - } - - void reset() { - mParent = null; - mView = null; - mTag = null; - mIcon = null; - mText = null; - mContentDesc = null; - mPosition = INVALID_POSITION; - mCustomView = null; - } - } - - class TabView extends LinearLayout { - private Tab mTab; - private TextView mTextView; - private ImageView mIconView; - - private View mCustomView; - private TextView mCustomTextView; - private ImageView mCustomIconView; - - private int mDefaultMaxLines = 2; - - public TabView(Context context) { - super(context); - if (mTabBackgroundResId != 0) { - ViewCompat.setBackground( - this, AppCompatResources.getDrawable(context, mTabBackgroundResId)); - } - ViewCompat.setPaddingRelative(this, mTabPaddingStart, mTabPaddingTop, - mTabPaddingEnd, mTabPaddingBottom); - setGravity(Gravity.CENTER_VERTICAL); - setOrientation(HORIZONTAL); - setClickable(true); - ViewCompat.setPointerIcon(this, - PointerIconCompat.getSystemIcon(getContext(), PointerIconCompat.TYPE_HAND)); - } - - @Override - public boolean performClick() { - final boolean handled = super.performClick(); - - if (mTab != null) { - if (!handled) { - playSoundEffect(SoundEffectConstants.CLICK); - } - mTab.select(); - return true; - } else { - return handled; - } - } - - @Override - public void setSelected(final boolean selected) { - final boolean changed = isSelected() != selected; - - super.setSelected(selected); - - if (changed && selected && Build.VERSION.SDK_INT < 16) { - // Pre-JB we need to manually send the TYPE_VIEW_SELECTED event - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); - } - - // Always dispatch this to the child views, regardless of whether the value has - // changed - if (mTextView != null) { - mTextView.setSelected(selected); - } - if (mIconView != null) { - mIconView.setSelected(selected); - } - if (mCustomView != null) { - mCustomView.setSelected(selected); - } - } - - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - // This view masquerades as an action bar tab. - event.setClassName(ActionBar.Tab.class.getName()); - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - // This view masquerades as an action bar tab. - info.setClassName(ActionBar.Tab.class.getName()); - } - - @Override - public void onMeasure(final int origWidthMeasureSpec, final int origHeightMeasureSpec) { - final int specWidthSize = MeasureSpec.getSize(origWidthMeasureSpec); - final int specWidthMode = MeasureSpec.getMode(origWidthMeasureSpec); - final int maxWidth = getTabMaxWidth(); - - final int widthMeasureSpec; - final int heightMeasureSpec = origHeightMeasureSpec; - - if (maxWidth > 0 && (specWidthMode == MeasureSpec.UNSPECIFIED - || specWidthSize > maxWidth)) { - // If we have a max width and a given spec which is either unspecified or - // larger than the max width, update the width spec using the same mode - widthMeasureSpec = MeasureSpec.makeMeasureSpec(mTabMaxWidth, MeasureSpec.AT_MOST); - } else { - // Else, use the original width spec - widthMeasureSpec = origWidthMeasureSpec; - } - - // Now lets measure - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - // We need to switch the text size based on whether the text is spanning 2 lines or not - if (mTextView != null) { - // final Resources res = getResources(); - float textSize = mTabTextSize; - int maxLines = mDefaultMaxLines; - - if (mIconView != null && mIconView.getVisibility() == VISIBLE) { - // If the icon view is being displayed, we limit the text to 1 line - maxLines = 2; - } else if (mTextView != null && mTextView.getLineCount() > 1) { - // Otherwise when we have text which wraps we reduce the text size - textSize = mTabTextMultiLineSize; - } - - final float curTextSize = mTextView.getTextSize(); - final int curLineCount = mTextView.getLineCount(); - final int curMaxLines = TextViewCompat.getMaxLines(mTextView); - - if (textSize != curTextSize || (curMaxLines >= 0 && maxLines != curMaxLines)) { - // We've got a new text size and/or max lines... - boolean updateTextView = true; - - if (mMode == MODE_FIXED && textSize > curTextSize && curLineCount == 1) { - // If we're in fixed mode, going up in text size and currently have 1 line - // then it's very easy to get into an infinite recursion. - // To combat that we check to see if the change in text size - // will cause a line count change. If so, abort the size change and stick - // to the smaller size. - final Layout layout = mTextView.getLayout(); - if (layout == null || approximateLineWidth(layout, 0, textSize) - > getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) { - updateTextView = false; - } - } - - if (updateTextView) { - mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); - mTextView.setMaxLines(maxLines); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - } - } - - void setTab(@Nullable final Tab tab) { - if (tab != mTab) { - mTab = tab; - update(); - } - } - - void reset() { - setTab(null); - setSelected(false); - } - - final void update() { - final Tab tab = mTab; - final View custom = tab != null ? tab.getCustomView() : null; - if (custom != null) { - final ViewParent customParent = custom.getParent(); - if (customParent != this) { - if (customParent != null) { - ((ViewGroup) customParent).removeView(custom); - } - addView(custom); - } - mCustomView = custom; - if (mTextView != null) { - mTextView.setVisibility(GONE); - } - if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - - mCustomTextView = (TextView) custom.findViewById(android.R.id.text1); - if (mCustomTextView != null) { - mDefaultMaxLines = TextViewCompat.getMaxLines(mCustomTextView); - } - mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon); - } else { - // We do not have a custom view. Remove one if it already exists - if (mCustomView != null) { - removeView(mCustomView); - mCustomView = null; - } - mCustomTextView = null; - mCustomIconView = null; - } - - if (mCustomView == null) { - // If there isn't a custom view, we'll us our own in-built layouts - if (mIconView == null) { - ImageView iconView = (ImageView) LayoutInflater.from(getContext()) - .inflate(R.layout.design_layout_tab_icon, this, false); - iconView.setLayoutParams(new LinearLayout.LayoutParams(dpToPx(25), - dpToPx(25))); - addView(iconView, 0); - mIconView = iconView; - } - if (mTextView == null) { - TextView textView = (TextView) LayoutInflater.from(getContext()) - .inflate(R.layout.design_layout_tab_text, this, false); - LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - textParams.gravity = Gravity.CENTER_VERTICAL; - textView.setLayoutParams(textParams); - addView(textView); - mTextView = textView; - mDefaultMaxLines = TextViewCompat.getMaxLines(mTextView); - } - TextViewCompat.setTextAppearance(mTextView, mTabTextAppearance); - if (mTabTextColors != null) { - mTextView.setTextColor(mTabTextColors); - } - updateTextAndIcon(mTextView, mIconView); - } else { - // Else, we'll see if there is a TextView or ImageView present and update them - if (mCustomTextView != null || mCustomIconView != null) { - updateTextAndIcon(mCustomTextView, mCustomIconView); - } - } - - // Finally update our selected state - setSelected(tab != null && tab.isSelected()); - } - - private void updateTextAndIcon(@Nullable final TextView textView, - @Nullable final ImageView iconView) { - final Drawable icon = mTab != null ? mTab.getIcon() : null; - final CharSequence text = mTab != null ? mTab.getText() : null; - final CharSequence contentDesc = mTab != null ? mTab.getContentDescription() : null; - - if (iconView != null) { - if (icon != null) { - iconView.setImageDrawable(icon); - iconView.setVisibility(VISIBLE); - setVisibility(VISIBLE); - } else { - iconView.setVisibility(GONE); - iconView.setImageDrawable(null); - } - iconView.setContentDescription(contentDesc); - } - - final boolean hasText = !TextUtils.isEmpty(text); - if (textView != null) { - if (hasText) { - textView.setText(text); - textView.setVisibility(VISIBLE); - setVisibility(VISIBLE); - } else { - textView.setVisibility(GONE); - textView.setText(null); - } - textView.setContentDescription(contentDesc); - } - - if (iconView != null) { - MarginLayoutParams lp = ((MarginLayoutParams) iconView.getLayoutParams()); - int bottomMargin = 0; - if (hasText && iconView.getVisibility() == VISIBLE) { - // If we're showing both text and icon, add some margin bottom to the icon - bottomMargin = dpToPx(DEFAULT_GAP_TEXT_ICON); - } - if (bottomMargin != lp.bottomMargin) { - lp.bottomMargin = bottomMargin; - iconView.requestLayout(); - } - } - TooltipCompat.setTooltipText(this, hasText ? null : contentDesc); - } - - public Tab getTab() { - return mTab; - } - - /** - * Approximates a given lines width with the new provided text size. - */ - private float approximateLineWidth(Layout layout, int line, float textSize) { - return layout.getLineWidth(line) * (textSize / layout.getPaint().getTextSize()); - } - } - - private class SlidingTabStrip extends LinearLayout { - private int mSelectedIndicatorHeight; - private final Paint mSelectedIndicatorPaint; - - int mSelectedPosition = -1; - float mSelectionOffset; - - private int mLayoutDirection = -1; - - private int mIndicatorTop = -1; - private int mIndicatorBottom = -1; - - private ValueAnimator mIndicatorAnimator; - - SlidingTabStrip(Context context) { - super(context); - setWillNotDraw(false); - - - // Easy way to get vertical tab view - setOrientation(VERTICAL); - - mSelectedIndicatorPaint = new Paint(); - } - - void setSelectedIndicatorColor(int color) { - if (mSelectedIndicatorPaint.getColor() != color) { - mSelectedIndicatorPaint.setColor(color); - ViewCompat.postInvalidateOnAnimation(this); - } - } - - void setSelectedIndicatorHeight(int height) { - if (mSelectedIndicatorHeight != height) { - mSelectedIndicatorHeight = height; - ViewCompat.postInvalidateOnAnimation(this); - } - } - - boolean childrenNeedLayout() { - for (int i = 0, z = getChildCount(); i < z; i++) { - final View child = getChildAt(i); - if (child.getWidth() <= 0) { - return true; - } - } - return false; - } - - void setIndicatorPositionFromTabPosition(int position, float positionOffset) { - if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) { - mIndicatorAnimator.cancel(); - } - - mSelectedPosition = position; - mSelectionOffset = positionOffset; - updateIndicatorPosition(); - } - - float getIndicatorPosition() { - return mSelectedPosition + mSelectionOffset; - } - - @Override - public void onRtlPropertiesChanged(int layoutDirection) { - super.onRtlPropertiesChanged(layoutDirection); - - // Workaround for a bug before Android M where LinearLayout did not relayout itself when - // layout direction changed. - if (Build.VERSION.SDK_INT < 23 /* Build.VERSION_CODES.M */) { - //noinspection WrongConstant - if (mLayoutDirection != layoutDirection) { - requestLayout(); - mLayoutDirection = layoutDirection; - } - } - } - - @Override - protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) { - // ScrollView will first measure use with UNSPECIFIED, and then with - // EXACTLY. Ignore the first call since anything we do will be overwritten anyway - return; - } - - if (mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER) { - final int count = getChildCount(); - - // First we'll find the widest tab - int largestTabWidth = 0; - for (int i = 0, z = count; i < z; i++) { - View child = getChildAt(i); - if (child.getVisibility() == VISIBLE) { - largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth()); - } - } - - if (largestTabWidth <= 0) { - // If we don't have a largest child yet, skip until the next measure pass - return; - } - - final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN); - boolean remeasure = false; - - if (largestTabWidth * count <= getMeasuredWidth() - gutter * 2) { - // If the tabs fit within our width minus gutters, we will set all tabs to have - // the same width - for (int i = 0; i < count; i++) { - final LinearLayout.LayoutParams lp = - (LayoutParams) getChildAt(i).getLayoutParams(); - if (lp.width != largestTabWidth || lp.weight != 0) { - lp.width = largestTabWidth; - lp.weight = 0; - remeasure = true; - } - } - } else { - // If the tabs will wrap to be larger than the width minus gutters, we need - // to switch to GRAVITY_FILL - mTabGravity = GRAVITY_FILL; - updateTabViews(false); - remeasure = true; - } - - if (remeasure) { - // Now re-measure after our changes - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - - if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) { - // If we're currently running an animation, lets cancel it and start a - // new animation with the remaining duration - mIndicatorAnimator.cancel(); - final long duration = mIndicatorAnimator.getDuration(); - animateIndicatorToPosition(mSelectedPosition, - Math.round((1f - mIndicatorAnimator.getAnimatedFraction()) * duration)); - } else { - // If we've been layed out, update the indicator position - updateIndicatorPosition(); - } - } - - private void updateIndicatorPosition() { - final View selectedTitle = getChildAt(mSelectedPosition); - int top, bottom; - - if (selectedTitle != null && selectedTitle.getWidth() > 0) { - top = selectedTitle.getTop(); - bottom = selectedTitle.getBottom(); - - if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) { - // Draw the selection partway between the tabs - View nextTitle = getChildAt(mSelectedPosition + 1); - top = (int) (mSelectionOffset * nextTitle.getTop() + - (1.0f - mSelectionOffset) * top); - bottom = (int) (mSelectionOffset * nextTitle.getBottom() + - (1.0f - mSelectionOffset) * bottom); - } - } else { - top = bottom = -1; - } - - setIndicatorPosition(top, bottom); - } - - void setIndicatorPosition(int top, int bottom) { - if (top != mIndicatorTop || bottom != mIndicatorBottom) { - // If the indicator's left/right has changed, invalidate - mIndicatorTop = top; - mIndicatorBottom = bottom; - ViewCompat.postInvalidateOnAnimation(this); - } - } - - void animateIndicatorToPosition(final int position, int duration) { - if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) { - mIndicatorAnimator.cancel(); - } - - final View targetView = getChildAt(position); - if (targetView == null) { - // If we don't have a view, just update the position now and return - updateIndicatorPosition(); - return; - } - - final int targetTop = targetView.getTop(); - final int targetBottom = targetView.getBottom(); - final int startTop; - final int startBottom; - - if (Math.abs(position - mSelectedPosition) <= 1) { - // If the views are adjacent, we'll animate from edge-to-edge - startTop = mIndicatorTop; - startBottom = mIndicatorBottom; - } else { - // Else, we'll just grow from the nearest edge - final int offset = dpToPx(MOTION_NON_ADJACENT_OFFSET); - if (position < mSelectedPosition) { - // We're going end-to-start - /* - if (isRtl) { - startTop = startBottom = targetTop - offset; - } else { - */ - startTop = startBottom = targetBottom + offset; - // } - } else { - // We're going start-to-end - /* - if (isRtl) { - startLeft = startRight = targetRight + offset; - } else { - */ - startTop = startBottom = targetTop - offset; - // } - } - } - - if (startTop != targetTop || startBottom != targetBottom) { - ValueAnimator animator = mIndicatorAnimator = new ValueAnimator(); - animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); - animator.setDuration(duration); - animator.setFloatValues(0, 1); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @SuppressLint("RestrictedApi") - @Override - public void onAnimationUpdate(ValueAnimator animator) { - final float fraction = animator.getAnimatedFraction(); - setIndicatorPosition( - AnimationUtils.lerp(startTop, targetTop, fraction), - AnimationUtils.lerp(startBottom, targetBottom, fraction)); - } - }); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - mSelectedPosition = position; - mSelectionOffset = 0f; - } - }); - animator.start(); - } - } - - @Override - public void draw(Canvas canvas) { - super.draw(canvas); - - // Thick colored line at the left side of the current selection - if (mIndicatorTop >= 0 && mIndicatorBottom > mIndicatorTop) { - canvas.drawRect(0, mIndicatorTop, - mSelectedIndicatorHeight, mIndicatorBottom, mSelectedIndicatorPaint); - } - } - } - - private static ColorStateList createColorStateList(int defaultColor, int selectedColor) { - final int[][] states = new int[2][]; - final int[] colors = new int[2]; - int i = 0; - - states[i] = SELECTED_STATE_SET; - colors[i] = selectedColor; - i++; - - // Default enabled state - states[i] = EMPTY_STATE_SET; - colors[i] = defaultColor; - i++; - - return new ColorStateList(states, colors); - } - - private int getDefaultHeight() { - boolean hasIconAndText = false; - for (int i = 0, count = mTabs.size(); i < count; i++) { - Tab tab = mTabs.get(i); - if (tab != null && tab.getIcon() != null && !TextUtils.isEmpty(tab.getText())) { - hasIconAndText = true; - break; - } - } - return hasIconAndText ? DEFAULT_HEIGHT_WITH_TEXT_ICON : DEFAULT_HEIGHT; - } - - private int getTabMinWidth() { - if (mRequestedTabMinWidth != INVALID_WIDTH) { - // If we have been given a min width, use it - return mRequestedTabMinWidth; - } - // Else, we'll use the default value - return mMode == MODE_SCROLLABLE ? mScrollableTabMinWidth : 0; - } - - @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - // We don't care about the layout params of any views added to us, since we don't actually - // add them. The only view we add is the SlidingTabStrip, which is done manually. - // We return the default layout params so that we don't blow up if we're given a TabItem - // without android:layout_* values. - return generateDefaultLayoutParams(); - } - - int getTabMaxWidth() { - return mTabMaxWidth; - } - - /** - * A {@link ViewPager.OnPageChangeListener} class which contains the - * necessary calls back to the provided {@link TabLayout} so that the tab position is - * kept in sync. - * - *

This class stores the provided VerticalTabLayout weakly, meaning that you can use - * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener) - * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and - * not cause a leak. - */ - public static class VerticalTabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener { - private final WeakReference mTabLayoutRef; - private int mPreviousScrollState; - private int mScrollState; - - public VerticalTabLayoutOnPageChangeListener(VerticalTabLayout tabLayout) { - mTabLayoutRef = new WeakReference<>(tabLayout); - } - - @Override - public void onPageScrollStateChanged(final int state) { - mPreviousScrollState = mScrollState; - mScrollState = state; - } - - @Override - public void onPageScrolled(final int position, final float positionOffset, - final int positionOffsetPixels) { - final VerticalTabLayout tabLayout = mTabLayoutRef.get(); - if (tabLayout != null) { - // Only update the text selection if we're not settling, or we are settling after - // being dragged - final boolean updateText = mScrollState != SCROLL_STATE_SETTLING || - mPreviousScrollState == SCROLL_STATE_DRAGGING; - // Update the indicator if we're not settling after being idle. This is caused - // from a setCurrentItem() call and will be handled by an animation from - // onPageSelected() instead. - final boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING - && mPreviousScrollState == SCROLL_STATE_IDLE); - tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator); - } - } - - @Override - public void onPageSelected(final int position) { - final VerticalTabLayout tabLayout = mTabLayoutRef.get(); - if (tabLayout != null && tabLayout.getSelectedTabPosition() != position - && position < tabLayout.getTabCount()) { - // Select the tab, only updating the indicator if we're not being dragged/settled - // (since onPageScrolled will handle that). - final boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE - || (mScrollState == SCROLL_STATE_SETTLING - && mPreviousScrollState == SCROLL_STATE_IDLE); - tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator); - } - } - - void reset() { - mPreviousScrollState = mScrollState = SCROLL_STATE_IDLE; - } - } - - /** - * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back - * to the provided {@link ViewPager} so that the tab position is kept in sync. - */ - public static class ViewPagerOnTabSelectedListener implements VerticalTabLayout.OnTabSelectedListener { - private final ViewPager mViewPager; - - public ViewPagerOnTabSelectedListener(ViewPager viewPager) { - mViewPager = viewPager; - } - - @Override - public void onTabSelected(VerticalTabLayout.Tab tab) { - mViewPager.setCurrentItem(tab.getPosition()); - } - - @Override - public void onTabUnselected(VerticalTabLayout.Tab tab) { - // No-op - } - - @Override - public void onTabReselected(VerticalTabLayout.Tab tab) { - // No-op - } - } - - private class PagerAdapterObserver extends DataSetObserver { - PagerAdapterObserver() { - } - - @Override - public void onChanged() { - populateFromPagerAdapter(); - } - - @Override - public void onInvalidated() { - populateFromPagerAdapter(); - } - } - - private class AdapterChangeListener implements ViewPager.OnAdapterChangeListener { - private boolean mAutoRefresh; - - AdapterChangeListener() { - } - - @Override - public void onAdapterChanged(@NonNull ViewPager viewPager, - @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter) { - if (mViewPager == viewPager) { - setPagerAdapter((ViewPagerAdapter) newAdapter, mAutoRefresh); - } - } - - void setAutoRefresh(boolean autoRefresh) { - mAutoRefresh = autoRefresh; - } - } - - public static class ViewPagerAdapter extends FragmentPagerAdapter { - List viewPagerList = new ArrayList<>(); - - public ViewPagerAdapter(FragmentManager fragmentManager) { - super(fragmentManager); - } - - @Override - public Fragment getItem(int position) { - return viewPagerList.get(position).fragment; - } - - @Override - public int getCount() { - return viewPagerList.size(); - } - - public int getIcon(int position) { - return viewPagerList.get(position).icon; - } - - @Override - public CharSequence getPageTitle(int position) { - return viewPagerList.get(position).title; - } - - public void addFragment(Fragment fragment, int icon, String name) { - ViewPagerItem item = new ViewPagerItem(); - item.fragment = fragment; - item.icon = icon; - item.title = name; - viewPagerList.add(item); - } - - public void setFragment(int index, Fragment fragment, int icon, String name) { - ViewPagerItem item = new ViewPagerItem(); - item.fragment = fragment; - item.icon = icon; - item.title = name; - viewPagerList.set(index, item); - } - - public void removeFragment(int index) { - viewPagerList.remove(index); - } - } - - public static class ViewPagerItem { - public Fragment fragment; - public String title; - public int icon; - } -} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseLauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseLauncherActivity.java index 0c1aa9655..668050da2 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseLauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/BaseLauncherActivity.java @@ -1,5 +1,7 @@ package net.kdt.pojavlaunch; +import static net.kdt.pojavlaunch.Tools.getFileName; + import android.app.*; import android.content.*; import android.database.Cursor; @@ -28,8 +30,6 @@ import org.apache.commons.io.IOUtils; public abstract class BaseLauncherActivity extends BaseActivity { public Button mPlayButton; - public ConsoleFragment mConsoleView; - public CrashFragment mCrashView; public ProgressBar mLaunchProgress; public Spinner mVersionSelector; public MultiRTConfigDialog mRuntimeConfigDialog; @@ -73,15 +73,12 @@ public abstract class BaseLauncherActivity extends BaseActivity { final EditText edit = new EditText(this); edit.setSingleLine(); edit.setHint("-jar/-cp /path/to/file.jar ..."); - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){ - @Override - public void onClick(DialogInterface di, int i) { - Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class); - intent.putExtra("skipDetectMod", true); - intent.putExtra("javaArgs", edit.getText().toString()); - startActivity(intent); - } - }); + builder.setPositiveButton(android.R.string.ok, (di, i) -> { + Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class); + intent.putExtra("skipDetectMod", true); + intent.putExtra("javaArgs", edit.getText().toString()); + startActivity(intent); + }); dialog = builder.create(); dialog.setView(edit); dialog.show(); @@ -104,7 +101,7 @@ public abstract class BaseLauncherActivity extends BaseActivity { v.setEnabled(false); mTask = new MinecraftDownloaderTask(this); mTask.execute(mProfile.selectedVersion); - mCrashView.resetCrashLog = true; + } } @@ -132,6 +129,7 @@ public abstract class BaseLauncherActivity extends BaseActivity { decorView.setSystemUiVisibility(uiOptions); System.out.println("call to onResume; E"); } + SharedPreferences.OnSharedPreferenceChangeListener listRefreshListener = null; @Override protected void onResumeFragments() { @@ -150,76 +148,11 @@ public abstract class BaseLauncherActivity extends BaseActivity { System.out.println("call to onResumeFragments"); mRuntimeConfigDialog = new MultiRTConfigDialog(); mRuntimeConfigDialog.prepare(this); - try{ - final ProgressDialog barrier = new ProgressDialog(this); - barrier.setMessage(getString(R.string.global_waiting)); - barrier.setProgressStyle(barrier.STYLE_SPINNER); - barrier.setCancelable(false); - barrier.show(); - new Thread(new Runnable(){ - - @Override - public void run() - { - while (mConsoleView == null) { - try { - Thread.sleep(20); - } catch (Throwable th) {} - } - - try { - Thread.sleep(100); - } catch (Throwable th) {} - - runOnUiThread(new Runnable() { - @Override - public void run() - { - try { - mConsoleView.putLog(""); - barrier.dismiss(); - } catch (Throwable th) { - startActivity(getIntent()); - finish(); - } - } - }); - } - }).start(); - - File lastCrashFile = Tools.lastFileModified(Tools.DIR_HOME_CRASH); - if(CrashFragment.isNewCrash(lastCrashFile) || !mCrashView.getLastCrash().isEmpty()){ - mCrashView.resetCrashLog = false; - initTabs(2); - - } /*else throw new Exception();*/ - } catch(Throwable e) { - e.printStackTrace(); - } + //TODO ADD CRASH CHECK AND FOCUS System.out.println("call to onResumeFragments; E"); } - public static String getFileName(Context ctx, Uri uri) { - String result = null; - if (uri.getScheme().equals("content")) { - Cursor cursor = ctx.getContentResolver().query(uri, null, null, null, null); - try { - if (cursor != null && cursor.moveToFirst()) { - result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } finally { - cursor.close(); - } - } - if (result == null) { - result = uri.getPath(); - int cut = result.lastIndexOf('/'); - if (cut != -1) { - result = result.substring(cut + 1); - } - } - return result; - } + @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode,resultCode,data); @@ -229,62 +162,58 @@ public abstract class BaseLauncherActivity extends BaseActivity { barrier.setProgressStyle(barrier.STYLE_SPINNER); barrier.setCancelable(false); barrier.show(); - if (requestCode == MultiRTConfigDialog.MULTIRT_PICK_RUNTIME) { - if (data != null) { - final Uri uri = data.getData(); - Thread t = new Thread(() -> { - try { - String name = getFileName(this, uri); - MultiRTUtils.installRuntimeNamed(getContentResolver().openInputStream(uri), name, - (resid, stuff) -> BaseLauncherActivity.this.runOnUiThread( - () -> barrier.setMessage(BaseLauncherActivity.this.getString(resid, stuff)))); - MultiRTUtils.postPrepare(BaseLauncherActivity.this, name); - } catch (IOException e) { - Tools.showError(BaseLauncherActivity.this - , e); - } - BaseLauncherActivity.this.runOnUiThread(new Runnable() { - @Override - public void run() { - barrier.dismiss(); - mRuntimeConfigDialog.refresh(); - mRuntimeConfigDialog.dialog.show(); - } - }); - }); - t.start(); - } - } else if (requestCode == RUN_MOD_INSTALLER) { - if (data != null) { - final Uri uri = data.getData(); - barrier.setMessage(BaseLauncherActivity.this.getString(R.string.multirt_progress_caching)); - Thread t = new Thread(()->{ - try { - final String name = getFileName(this, uri); - final File modInstallerFile = new File(getCacheDir(), name); - FileOutputStream fos = new FileOutputStream(modInstallerFile); - IOUtils.copy(getContentResolver().openInputStream(uri), fos); - fos.close(); - BaseLauncherActivity.this.runOnUiThread(() -> { - barrier.dismiss(); - Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class); - intent.putExtra("modFile", modInstallerFile); - startActivity(intent); - }); - }catch(IOException e) { - Tools.showError(BaseLauncherActivity.this,e); - } - }); - t.start(); - } - } - } - } - // Catching touch exception - @Override - public boolean onTouchEvent(MotionEvent event) { - return super.onTouchEvent(event); + // Install the runtime + if (requestCode == MultiRTConfigDialog.MULTIRT_PICK_RUNTIME) { + if (data == null) return; + + final Uri uri = data.getData(); + Thread t = new Thread(() -> { + try { + String name = getFileName(this, uri); + MultiRTUtils.installRuntimeNamed(getContentResolver().openInputStream(uri), name, + (resid, stuff) -> BaseLauncherActivity.this.runOnUiThread( + () -> barrier.setMessage(BaseLauncherActivity.this.getString(resid, stuff)))); + MultiRTUtils.postPrepare(BaseLauncherActivity.this, name); + } catch (IOException e) { + Tools.showError(BaseLauncherActivity.this, e); + } + BaseLauncherActivity.this.runOnUiThread(() -> { + barrier.dismiss(); + mRuntimeConfigDialog.refresh(); + mRuntimeConfigDialog.dialog.show(); + }); + }); + t.start(); + } + + // Run a mod installer + if (requestCode == RUN_MOD_INSTALLER) { + if (data == null) return; + + final Uri uri = data.getData(); + barrier.setMessage(BaseLauncherActivity.this.getString(R.string.multirt_progress_caching)); + Thread t = new Thread(()->{ + try { + final String name = getFileName(this, uri); + final File modInstallerFile = new File(getCacheDir(), name); + FileOutputStream fos = new FileOutputStream(modInstallerFile); + IOUtils.copy(getContentResolver().openInputStream(uri), fos); + fos.close(); + BaseLauncherActivity.this.runOnUiThread(() -> { + barrier.dismiss(); + Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class); + intent.putExtra("modFile", modInstallerFile); + startActivity(intent); + }); + }catch(IOException e) { + Tools.showError(BaseLauncherActivity.this,e); + } + }); + t.start(); + } + + } } protected abstract void initTabs(int pageIndex); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java index 0941f55f5..c1055c9d7 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java @@ -1,5 +1,10 @@ package net.kdt.pojavlaunch; +import static android.os.Build.VERSION_CODES.P; +import static net.kdt.pojavlaunch.Tools.ignoreNotch; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_HIDE_SIDEBAR; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE; + import android.animation.ValueAnimator; import android.content.res.Configuration; import android.graphics.Color; @@ -8,7 +13,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.support.design.widget.VerticalTabLayout.ViewPagerAdapter; import android.util.Log; import android.view.View; import android.widget.AdapterView; @@ -19,54 +23,72 @@ import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; - import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Guideline; -import androidx.viewpager.widget.ViewPager; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; +import net.kdt.pojavlaunch.extra.ExtraCore; +import net.kdt.pojavlaunch.extra.ExtraListener; import net.kdt.pojavlaunch.fragments.ConsoleFragment; import net.kdt.pojavlaunch.fragments.CrashFragment; import net.kdt.pojavlaunch.fragments.LauncherFragment; -import net.kdt.pojavlaunch.prefs.LauncherPreferenceFragment; import net.kdt.pojavlaunch.prefs.LauncherPreferences; +import net.kdt.pojavlaunch.prefs.screens.LauncherPreferenceFragment; import net.kdt.pojavlaunch.value.MinecraftAccount; import java.io.File; import java.util.ArrayList; import java.util.List; -import static android.os.Build.VERSION_CODES.P; -import static net.kdt.pojavlaunch.Tools.ignoreNotch; -import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_HIDE_SIDEBAR; -import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_IGNORE_NOTCH; -import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE; - public class PojavLauncherActivity extends BaseLauncherActivity { - private ViewPager viewPager; + // An equivalent ViewPager2 adapter class + private static class ScreenSlidePagerAdapter extends FragmentStateAdapter { + public ScreenSlidePagerAdapter(FragmentActivity fa) { + super(fa); + } + + @Override + public Fragment createFragment(int position) { + if (position == 0) return new LauncherFragment(); + if (position == 1) return new ConsoleFragment(); + if (position == 2) return new CrashFragment(); + if (position == 3) return new LauncherPreferenceFragment(); + return null; + } + + @Override + public int getItemCount() { + return 4; + } + } + private TextView tvConnectStatus; private Spinner accountSelector; - private ViewPagerAdapter viewPageAdapter; + private ViewPager2 viewPager; private final Button[] Tabs = new Button[4]; - private View selected; + private View selectedTab; private ImageView accountFaceImageView; private Button logoutBtn; // MineButtons + private ExtraListener backPreferenceListener; public PojavLauncherActivity() { } @Override - protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.launcher_main_v4); //Boilerplate linking/initialisation viewPager = findViewById(R.id.launchermainTabPager); - selected = findViewById(R.id.viewTabSelected); + selectedTab = findViewById(R.id.viewTabSelected); tvConnectStatus = findViewById(R.id.launchermain_text_accountstatus); accountFaceImageView = findViewById(R.id.launchermain_account_image); accountSelector = findViewById(R.id.launchermain_spinner_account); @@ -85,48 +107,46 @@ public class PojavLauncherActivity extends BaseLauncherActivity Toast.makeText(this, "Launcher process id: " + android.os.Process.myPid(), Toast.LENGTH_LONG).show(); } - mConsoleView = new ConsoleFragment(); - mCrashView = new CrashFragment(); - - viewPageAdapter = new ViewPagerAdapter(getSupportFragmentManager()); - viewPageAdapter.addFragment(new LauncherFragment(), 0, getString(R.string.mcl_tab_news)); - viewPageAdapter.addFragment(mConsoleView, 0, getString(R.string.mcl_tab_console)); - viewPageAdapter.addFragment(mCrashView, 0, getString(R.string.mcl_tab_crash)); - viewPageAdapter.addFragment(new LauncherPreferenceFragment(), 0, getString(R.string.mcl_option_settings)); - - viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + // Setup the viewPager to slide across fragments + viewPager.setAdapter(new ScreenSlidePagerAdapter(this)); + viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { setTabActive(position); } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){} - - @Override - public void onPageScrollStateChanged(int state) {} }); - viewPager.setAdapter(viewPageAdapter); + initTabs(0); + + //Setup listener to the backPreference system + backPreferenceListener = new ExtraListener() { + @Override + public boolean onValueSet(String key, String value) { + if(value.equals("true")){ + onBackPressed(); + ExtraCore.setValue(key, "false"); + } + return false; + } + }; + ExtraCore.addExtraListener("back_preference", backPreferenceListener); - pickAccount(); - - - final List accountList = new ArrayList(); - final MinecraftAccount tempProfile = PojavProfile.getTempProfileContent(this); + // Try to load the temporary account + final List accountList = new ArrayList<>(); + final MinecraftAccount tempProfile = PojavProfile.getTempProfileContent(); if (tempProfile != null) { accountList.add(tempProfile.username); } for (String s : new File(Tools.DIR_ACCOUNT_NEW).list()) { accountList.add(s.substring(0, s.length() - 5)); } - + + // Setup account spinner + pickAccount(); ArrayAdapter adapterAcc = new ArrayAdapter(this, android.R.layout.simple_spinner_item, accountList); adapterAcc.setDropDownViewResource(android.R.layout.simple_list_item_single_choice); - - //TODO AUTHENTICATION WHEN CHANGING ACCOUNT - //TODO SHOW ACCOUNT IMAGE IF AVAILABLE accountSelector.setAdapter(adapterAcc); + if (tempProfile != null) { accountSelector.setSelection(0); } else { @@ -155,7 +175,8 @@ public class PojavLauncherActivity extends BaseLauncherActivity // TODO: Implement this method } }); - + + // Setup the minecraft version list List versions = new ArrayList<>(); final File fVers = new File(Tools.DIR_HOME_VERSION); @@ -183,7 +204,6 @@ public class PojavLauncherActivity extends BaseLauncherActivity statusIsLaunching(false); - initTabs(0); //Add the preference changed listener LauncherPreferences.DEFAULT_PREF.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> { @@ -247,9 +267,9 @@ public class PojavLauncherActivity extends BaseLauncherActivity Tabs[index].setTextColor(Color.WHITE); //Animating the white bar on the left - ValueAnimator animation = ValueAnimator.ofFloat(selected.getY(), Tabs[index].getY()+(Tabs[index].getHeight()-selected.getHeight())/2f); + ValueAnimator animation = ValueAnimator.ofFloat(selectedTab.getY(), Tabs[index].getY()+(Tabs[index].getHeight()- selectedTab.getHeight())/2f); animation.setDuration(250); - animation.addUpdateListener(animation1 -> selected.setY((float) animation1.getAnimatedValue())); + animation.addUpdateListener(animation1 -> selectedTab.setY((float) animation1.getAnimatedValue())); animation.start(); } @@ -272,7 +292,7 @@ public class PojavLauncherActivity extends BaseLauncherActivity guideLine.setLayoutParams(params); //Remove the selected Tab and the head image - selected.setVisibility(View.GONE); + selectedTab.setVisibility(View.GONE); accountFaceImageView.setVisibility(View.GONE); //Enlarge the button, but just a bit. @@ -285,7 +305,7 @@ public class PojavLauncherActivity extends BaseLauncherActivity guideLine.setLayoutParams(params); //Show the selected Tab - selected.setVisibility(View.VISIBLE); + selectedTab.setVisibility(View.VISIBLE); accountFaceImageView.setVisibility(View.VISIBLE); //Set the default button size @@ -310,5 +330,22 @@ public class PojavLauncherActivity extends BaseLauncherActivity } } + /** + * Custom back stack system. Use the classic backstack when the focus is on the setting screen, + * finish the activity and remove the back_preference listener otherwise + */ + @Override + public void onBackPressed() { + int count = getSupportFragmentManager().getBackStackEntryCount(); + + if (count == 0 && viewPager.getCurrentItem() == 3) { + super.onBackPressed(); + //additional code + ExtraCore.removeExtraListener("back_preference", backPreferenceListener); + finish(); + } else { + getSupportFragmentManager().popBackStack(); + } + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavProfile.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavProfile.java index 999651a78..8a6c1b339 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavProfile.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavProfile.java @@ -25,13 +25,13 @@ public class PojavProfile public static MinecraftAccount getCurrentProfileContent(Context ctx) throws JsonSyntaxException { MinecraftAccount build = MinecraftAccount.load(getCurrentProfileName(ctx)); if (build == null) { - System.out.println("isTempProfile null? " + (getTempProfileContent(ctx) == null)); - return getTempProfileContent(ctx); + System.out.println("isTempProfile null? " + (getTempProfileContent() == null)); + return getTempProfileContent(); } return build; } - public static MinecraftAccount getTempProfileContent(Context ctx) { + public static MinecraftAccount getTempProfileContent() { try { MinecraftAccount acc = MinecraftAccount.parse(Tools.read(Tools.DIR_DATA+"/cache/tempacc.json")); if (acc.accessToken == null) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferenceFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferenceFragment.java deleted file mode 100644 index fca26f1c9..000000000 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferenceFragment.java +++ /dev/null @@ -1,104 +0,0 @@ -package net.kdt.pojavlaunch.prefs; - - -import android.graphics.Color; -import android.os.*; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.*; -import net.kdt.pojavlaunch.R; -import net.kdt.pojavlaunch.fragments.LauncherFragment; - -import android.content.*; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import static net.kdt.pojavlaunch.Architecture.is32BitsDevice; -import static net.kdt.pojavlaunch.Tools.getTotalDeviceMemory; -import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE; - -public class LauncherPreferenceFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener -{ - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - view.setBackgroundColor(Color.parseColor("#44000000")); - super.onViewCreated(view, savedInstanceState); - } - - @Override - public void onCreatePreferences(Bundle b, String str) { - addPreferencesFromResource(R.xml.pref_main); - - //Disable notch checking behavior on android 8.1 and below. - findPreference("ignoreNotch").setVisible(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && PREF_NOTCH_SIZE != 0); - - CustomSeekBarPreference seek2 = findPreference("timeLongPressTrigger"); - seek2.setRange(100, 1000); - seek2.setValue(LauncherPreferences.PREF_LONGPRESS_TRIGGER); - seek2.setSuffix(" ms"); - - CustomSeekBarPreference seek3 = findPreference("buttonscale"); - seek3.setRange(80, 250); - seek3.setValue((int) LauncherPreferences.PREF_BUTTONSIZE); - seek3.setSuffix(" %"); - - CustomSeekBarPreference seek4 = findPreference("mousescale"); - seek4.setRange(25, 300); - seek4.setValue((int) LauncherPreferences.PREF_MOUSESCALE); - seek4.setSuffix(" %"); - - CustomSeekBarPreference seek5 = findPreference("resolutionRatio"); - seek5.setMin(25); - seek5.setSuffix(" %"); - - CustomSeekBarPreference seek6 = findPreference("mousespeed"); - seek6.setRange(25, 300); - seek6.setValue((int)(LauncherPreferences.PREF_MOUSESPEED*100f)); - seek6.setSuffix(" %"); - - - int maxRAM; - int deviceRam = getTotalDeviceMemory(getContext()); - - - CustomSeekBarPreference seek7 = findPreference("allocation"); - seek7.setMin(256); - - if(is32BitsDevice()) maxRAM = Math.min(1100, deviceRam); - else maxRAM = deviceRam - (deviceRam < 3064 ? 800 : 1024); //To have a minimum for the device to breathe - - seek7.setMax(maxRAM); - seek7.setValue(LauncherPreferences.PREF_RAM_ALLOCATION); - seek7.setSuffix(" MB"); - - // #724 bug fix - if (seek5.getValue() < 25) { - seek5.setValue(100); - } - - EditTextPreference editJVMArgs = findPreference("javaArgs"); - if (editJVMArgs != null) { - editJVMArgs.setOnBindEditTextListener((editText) -> editText.setSingleLine()); - } - } - - @Override - public void onResume() { - super.onResume(); - getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); - } - - @Override - public void onPause() { - getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); - super.onPause(); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences p, String s) { - LauncherPreferences.loadPreferences(getContext()); - } -} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/MinecraftDownloaderTask.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/MinecraftDownloaderTask.java index 98b24549e..0c001a0d8 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/MinecraftDownloaderTask.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/MinecraftDownloaderTask.java @@ -324,7 +324,7 @@ public class MinecraftDownloaderTask extends AsyncTask - - - - + + - - + - + + - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file