From cbdaa48705855169827f29014efbaa934d212ccf Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 12 Nov 2019 22:58:14 +0100 Subject: Convert most fragments/activities to androidx --- .../support/v4n/app/FragmentStatePagerAdapter.java | 232 -- .../android/support/v4n/view/PagerAdapter.java | 320 --- .../java/android/support/v4n/view/ViewPager.java | 2896 -------------------- .../de/blinkt/openvpn/activities/BaseActivity.java | 15 +- .../blinkt/openvpn/activities/ConfigConverter.kt | 8 +- .../de/blinkt/openvpn/activities/LogWindow.java | 2 +- .../de/blinkt/openvpn/activities/MainActivity.java | 28 +- .../blinkt/openvpn/activities/VPNPreferences.java | 56 +- .../de/blinkt/openvpn/fragments/AboutFragment.java | 7 +- .../de/blinkt/openvpn/fragments/FaqFragment.java | 5 +- .../blinkt/openvpn/fragments/GeneralSettings.java | 208 +- .../de/blinkt/openvpn/fragments/GraphFragment.java | 13 +- .../openvpn/fragments/KeyChainSettingsFragment.kt | 20 +- .../de/blinkt/openvpn/fragments/LogFragment.java | 39 +- .../fragments/OpenVpnPreferencesFragment.java | 6 +- .../blinkt/openvpn/fragments/SendDumpFragment.java | 184 +- .../openvpn/fragments/Settings_Allowed_Apps.kt | 24 +- .../openvpn/fragments/Settings_Authentication.java | 300 +- .../openvpn/fragments/Settings_Fragment.java | 3 +- .../de/blinkt/openvpn/fragments/Settings_IP.java | 40 +- .../blinkt/openvpn/fragments/Settings_Obscure.java | 18 +- .../blinkt/openvpn/fragments/Settings_Routing.java | 34 +- .../openvpn/fragments/ShowConfigFragment.java | 24 +- .../blinkt/openvpn/fragments/VPNProfileList.java | 12 +- .../openvpn/views/DefaultVPNListPreference.java | 4 +- .../blinkt/openvpn/views/PagerSlidingTabStrip.java | 732 ----- .../blinkt/openvpn/views/RemoteCNPreference.java | 172 +- .../openvpn/views/RemoteCNPreferenceDialog.java | 124 + .../openvpn/views/ScreenSlidePagerAdapter.java | 11 +- .../de/blinkt/openvpn/views/SlidingTabLayout.java | 314 --- .../de/blinkt/openvpn/views/SlidingTabStrip.java | 207 -- .../java/de/blinkt/openvpn/views/TabBarView.java | 16 - 32 files changed, 687 insertions(+), 5387 deletions(-) delete mode 100644 main/src/ui/java/android/support/v4n/app/FragmentStatePagerAdapter.java delete mode 100644 main/src/ui/java/android/support/v4n/view/PagerAdapter.java delete mode 100644 main/src/ui/java/android/support/v4n/view/ViewPager.java delete mode 100644 main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java create mode 100644 main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreferenceDialog.java delete mode 100644 main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java delete mode 100644 main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java delete mode 100644 main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java (limited to 'main/src/ui/java') diff --git a/main/src/ui/java/android/support/v4n/app/FragmentStatePagerAdapter.java b/main/src/ui/java/android/support/v4n/app/FragmentStatePagerAdapter.java deleted file mode 100644 index 98093854..00000000 --- a/main/src/ui/java/android/support/v4n/app/FragmentStatePagerAdapter.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2011 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.v4n.app; - -import java.util.ArrayList; - -import android.annotation.TargetApi; -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcelable; -import android.support.v4n.view.PagerAdapter; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; - -/** - * Implementation of {@link androidx.viewpager.widget.PagerAdapter} that - * uses a {@link Fragment} to manage each page. This class also handles - * saving and restoring of fragment's state. - * - *

This version of the pager is more useful when there are a large number - * of pages, working more like a list view. When pages are not visible to - * the user, their entire fragment may be destroyed, only keeping the saved - * state of that fragment. This allows the pager to hold on to much less - * memory associated with each visited page as compared to - * {@link FragmentPagerAdapter} at the cost of potentially more overhead when - * switching between pages. - * - *

When using FragmentPagerAdapter the host ViewPager must have a - * valid ID set.

- * - *

Subclasses only need to implement {@link #getItem(int)} - * and {@link #getCount()} to have a working adapter. - * - *

Here is an example implementation of a pager containing fragments of - * lists: - * - * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java - * complete} - * - *

The R.layout.fragment_pager resource of the top-level fragment is: - * - * {@sample development/samples/Support13Demos/res/layout/fragment_pager.xml - * complete} - * - *

The R.layout.fragment_pager_list resource containing each - * individual fragment's layout is: - * - * {@sample development/samples/Support13Demos/res/layout/fragment_pager_list.xml - * complete} - */ -public abstract class FragmentStatePagerAdapter extends PagerAdapter { - private static final String TAG = "FragmentStatePagerAdptr"; - private static final boolean DEBUG = false; - - private final FragmentManager mFragmentManager; - private FragmentTransaction mCurTransaction = null; - - private ArrayList mSavedState = new ArrayList(); - private ArrayList mFragments = new ArrayList(); - private Fragment mCurrentPrimaryItem = null; - - public FragmentStatePagerAdapter(FragmentManager fm) { - mFragmentManager = fm; - } - - /** - * Return the Fragment associated with a specified position. - */ - public abstract Fragment getItem(int position); - - @Override - public void startUpdate(ViewGroup container) { - } - - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) - @Override - public Object instantiateItem(ViewGroup container, int position) { - // If we already have this item instantiated, there is nothing - // to do. This can happen when we are restoring the entire pager - // from its saved state, where the fragment manager has already - // taken care of restoring the fragments we previously had instantiated. - if (mFragments.size() > position) { - Fragment f = mFragments.get(position); - if (f != null) { - return f; - } - } - - if (mCurTransaction == null) { - mCurTransaction = mFragmentManager.beginTransaction(); - } - - Fragment fragment = getItem(position); - if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); - if (mSavedState.size() > position) { - Fragment.SavedState fss = mSavedState.get(position); - if (fss != null) { - fragment.setInitialSavedState(fss); - } - } - while (mFragments.size() <= position) { - mFragments.add(null); - } - fragment.setMenuVisibility(false); - fragment.setUserVisibleHint(false); - mFragments.set(position, fragment); - mCurTransaction.add(container.getId(), fragment); - - return fragment; - } - - @Override - public void destroyItem(ViewGroup container, int position, Object object) { - Fragment fragment = (Fragment)object; - - if (mCurTransaction == null) { - mCurTransaction = mFragmentManager.beginTransaction(); - } - if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object - + " v=" + ((Fragment)object).getView()); - while (mSavedState.size() <= position) { - mSavedState.add(null); - } - mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); - mFragments.set(position, null); - - mCurTransaction.remove(fragment); - } - - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) - @Override - public void setPrimaryItem(ViewGroup container, int position, Object object) { - Fragment fragment = (Fragment)object; - if (fragment != mCurrentPrimaryItem) { - if (mCurrentPrimaryItem != null) { - mCurrentPrimaryItem.setMenuVisibility(false); - mCurrentPrimaryItem.setUserVisibleHint(false); - } - if (fragment != null) { - fragment.setMenuVisibility(true); - fragment.setUserVisibleHint(true); - } - mCurrentPrimaryItem = fragment; - } - } - - @Override - public void finishUpdate(ViewGroup container) { - if (mCurTransaction != null) { - mCurTransaction.commitAllowingStateLoss(); - mCurTransaction = null; - mFragmentManager.executePendingTransactions(); - } - } - - @Override - public boolean isViewFromObject(View view, Object object) { - return ((Fragment)object).getView() == view; - } - - @Override - public Parcelable saveState() { - Bundle state = null; - if (mSavedState.size() > 0) { - state = new Bundle(); - Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; - mSavedState.toArray(fss); - state.putParcelableArray("states", fss); - } - for (int i=0; i keys = bundle.keySet(); - for (String key: keys) { - if (key.startsWith("f")) { - int index = Integer.parseInt(key.substring(1)); - Fragment f = mFragmentManager.getFragment(bundle, key); - if (f != null) { - while (mFragments.size() <= index) { - mFragments.add(null); - } - f.setMenuVisibility(false); - mFragments.set(index, f); - } else { - Log.w(TAG, "Bad fragment at key " + key); - } - } - } - } - } -} diff --git a/main/src/ui/java/android/support/v4n/view/PagerAdapter.java b/main/src/ui/java/android/support/v4n/view/PagerAdapter.java deleted file mode 100644 index 79177c02..00000000 --- a/main/src/ui/java/android/support/v4n/view/PagerAdapter.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 2011 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.v4n.view; - -import android.database.DataSetObservable; -import android.database.DataSetObserver; -import android.os.Parcelable; -import android.view.View; -import android.view.ViewGroup; - -/** - * Base class providing the adapter to populate pages inside of - * a {@link ViewPager}. You will most likely want to use a more - * specific implementation of this, such as - * {@link android.support.v4n.app.FragmentPagerAdapter} or - * {@link androidx.core.app.FragmentStatePagerAdapter}. - * - *

When you implement a PagerAdapter, you must override the following methods - * at minimum:

- * - * - *

PagerAdapter is more general than the adapters used for - * {@link android.widget.AdapterView AdapterViews}. Instead of providing a - * View recycling mechanism directly ViewPager uses callbacks to indicate the - * steps taken during an update. A PagerAdapter may implement a form of View - * recycling if desired or use a more sophisticated method of managing page - * Views such as Fragment transactions where each page is represented by its - * own Fragment.

- * - *

ViewPager associates each page with a key Object instead of working with - * Views directly. This key is used to track and uniquely identify a given page - * independent of its position in the adapter. A call to the PagerAdapter method - * {@link #startUpdate(ViewGroup)} indicates that the contents of the ViewPager - * are about to change. One or more calls to {@link #instantiateItem(ViewGroup, int)} - * and/or {@link #destroyItem(ViewGroup, int, Object)} will follow, and the end - * of an update will be signaled by a call to {@link #finishUpdate(ViewGroup)}. - * By the time {@link #finishUpdate(ViewGroup) finishUpdate} returns the views - * associated with the key objects returned by - * {@link #instantiateItem(ViewGroup, int) instantiateItem} should be added to - * the parent ViewGroup passed to these methods and the views associated with - * the keys passed to {@link #destroyItem(ViewGroup, int, Object) destroyItem} - * should be removed. The method {@link #isViewFromObject(View, Object)} identifies - * whether a page View is associated with a given key object.

- * - *

A very simple PagerAdapter may choose to use the page Views themselves - * as key objects, returning them from {@link #instantiateItem(ViewGroup, int)} - * after creation and adding them to the parent ViewGroup. A matching - * {@link #destroyItem(ViewGroup, int, Object)} implementation would remove the - * View from the parent ViewGroup and {@link #isViewFromObject(View, Object)} - * could be implemented as return view == object;.

- * - *

PagerAdapter supports data set changes. Data set changes must occur on the - * main thread and must end with a call to {@link #notifyDataSetChanged()} similar - * to AdapterView adapters derived from {@link android.widget.BaseAdapter}. A data - * set change may involve pages being added, removed, or changing position. The - * ViewPager will keep the current page active provided the adapter implements - * the method {@link #getItemPosition(Object)}.

- */ -public abstract class PagerAdapter { - private DataSetObservable mObservable = new DataSetObservable(); - - public static final int POSITION_UNCHANGED = -1; - public static final int POSITION_NONE = -2; - - /** - * Return the number of views available. - */ - public abstract int getCount(); - - /** - * Called when a change in the shown pages is going to start being made. - * @param container The containing View which is displaying this adapter's - * page views. - */ - public void startUpdate(ViewGroup container) { - startUpdate((View) container); - } - - /** - * Create the page for the given position. The adapter is responsible - * for adding the view to the container given here, although it only - * must ensure this is done by the time it returns from - * {@link #finishUpdate(ViewGroup)}. - * - * @param container The containing View in which the page will be shown. - * @param position The page position to be instantiated. - * @return Returns an Object representing the new page. This does not - * need to be a View, but can be some other container of the page. - */ - public Object instantiateItem(ViewGroup container, int position) { - return instantiateItem((View) container, position); - } - - /** - * Remove a page for the given position. The adapter is responsible - * for removing the view from its container, although it only must ensure - * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}. - * - * @param container The containing View from which the page will be removed. - * @param position The page position to be removed. - * @param object The same object that was returned by - * {@link #instantiateItem(View, int)}. - */ - public void destroyItem(ViewGroup container, int position, Object object) { - destroyItem((View) container, position, object); - } - - /** - * Called to inform the adapter of which item is currently considered to - * be the "primary", that is the one show to the user as the current page. - * - * @param container The containing View from which the page will be removed. - * @param position The page position that is now the primary. - * @param object The same object that was returned by - * {@link #instantiateItem(View, int)}. - */ - public void setPrimaryItem(ViewGroup container, int position, Object object) { - setPrimaryItem((View) container, position, object); - } - - /** - * Called when the a change in the shown pages has been completed. At this - * point you must ensure that all of the pages have actually been added or - * removed from the container as appropriate. - * @param container The containing View which is displaying this adapter's - * page views. - */ - public void finishUpdate(ViewGroup container) { - finishUpdate((View) container); - } - - /** - * Called when a change in the shown pages is going to start being made. - * @param container The containing View which is displaying this adapter's - * page views. - * - * @deprecated Use {@link #startUpdate(ViewGroup)} - */ - public void startUpdate(View container) { - } - - /** - * Create the page for the given position. The adapter is responsible - * for adding the view to the container given here, although it only - * must ensure this is done by the time it returns from - * {@link #finishUpdate(ViewGroup)}. - * - * @param container The containing View in which the page will be shown. - * @param position The page position to be instantiated. - * @return Returns an Object representing the new page. This does not - * need to be a View, but can be some other container of the page. - * - * @deprecated Use {@link #instantiateItem(ViewGroup, int)} - */ - public Object instantiateItem(View container, int position) { - throw new UnsupportedOperationException( - "Required method instantiateItem was not overridden"); - } - - /** - * Remove a page for the given position. The adapter is responsible - * for removing the view from its container, although it only must ensure - * this is done by the time it returns from {@link #finishUpdate(View)}. - * - * @param container The containing View from which the page will be removed. - * @param position The page position to be removed. - * @param object The same object that was returned by - * {@link #instantiateItem(View, int)}. - * - * @deprecated Use {@link #destroyItem(ViewGroup, int, Object)} - */ - public void destroyItem(View container, int position, Object object) { - throw new UnsupportedOperationException("Required method destroyItem was not overridden"); - } - - /** - * Called to inform the adapter of which item is currently considered to - * be the "primary", that is the one show to the user as the current page. - * - * @param container The containing View from which the page will be removed. - * @param position The page position that is now the primary. - * @param object The same object that was returned by - * {@link #instantiateItem(View, int)}. - * - * @deprecated Use {@link #setPrimaryItem(ViewGroup, int, Object)} - */ - public void setPrimaryItem(View container, int position, Object object) { - } - - /** - * Called when the a change in the shown pages has been completed. At this - * point you must ensure that all of the pages have actually been added or - * removed from the container as appropriate. - * @param container The containing View which is displaying this adapter's - * page views. - * - * @deprecated Use {@link #finishUpdate(ViewGroup)} - */ - public void finishUpdate(View container) { - } - - /** - * Determines whether a page View is associated with a specific key object - * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is - * required for a PagerAdapter to function properly. - * - * @param view Page View to check for association with object - * @param object Object to check for association with view - * @return true if view is associated with the key object object - */ - public abstract boolean isViewFromObject(View view, Object object); - - /** - * Save any instance state associated with this adapter and its pages that should be - * restored if the current UI state needs to be reconstructed. - * - * @return Saved state for this adapter - */ - public Parcelable saveState() { - return null; - } - - /** - * Restore any instance state associated with this adapter and its pages - * that was previously saved by {@link #saveState()}. - * - * @param state State previously saved by a call to {@link #saveState()} - * @param loader A ClassLoader that should be used to instantiate any restored objects - */ - public void restoreState(Parcelable state, ClassLoader loader) { - } - - /** - * Called when the host view is attempting to determine if an item's position - * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given - * item has not changed or {@link #POSITION_NONE} if the item is no longer present - * in the adapter. - * - *

The default implementation assumes that items will never - * change position and always returns {@link #POSITION_UNCHANGED}. - * - * @param object Object representing an item, previously returned by a call to - * {@link #instantiateItem(View, int)}. - * @return object's new position index from [0, {@link #getCount()}), - * {@link #POSITION_UNCHANGED} if the object's position has not changed, - * or {@link #POSITION_NONE} if the item is no longer present. - */ - public int getItemPosition(Object object) { - return POSITION_UNCHANGED; - } - - /** - * This method should be called by the application if the data backing this adapter has changed - * and associated views should update. - */ - public void notifyDataSetChanged() { - mObservable.notifyChanged(); - } - - /** - * Register an observer to receive callbacks related to the adapter's data changing. - * - * @param observer The {@link android.database.DataSetObserver} which will receive callbacks. - */ - public void registerDataSetObserver(DataSetObserver observer) { - mObservable.registerObserver(observer); - } - - /** - * Unregister an observer from callbacks related to the adapter's data changing. - * - * @param observer The {@link android.database.DataSetObserver} which will be unregistered. - */ - public void unregisterDataSetObserver(DataSetObserver observer) { - mObservable.unregisterObserver(observer); - } - - /** - * This method may be called by the ViewPager to obtain a title string - * to describe the specified page. This method may return null - * indicating no title for this page. The default implementation returns - * null. - * - * @param position The position of the title requested - * @return A title for the requested page - */ - public CharSequence getPageTitle(int position) { - return null; - } - - /** - * Returns the proportional width of a given page as a percentage of the - * ViewPager's measured width from (0.f-1.f] - * - * @param position The position of the page requested - * @return Proportional width for the given page position - */ - public float getPageWidth(int position) { - return 1.f; - } -} diff --git a/main/src/ui/java/android/support/v4n/view/ViewPager.java b/main/src/ui/java/android/support/v4n/view/ViewPager.java deleted file mode 100644 index aa360aa2..00000000 --- a/main/src/ui/java/android/support/v4n/view/ViewPager.java +++ /dev/null @@ -1,2896 +0,0 @@ -/* - * Copyright (C) 2011 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.v4n.view; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.DataSetObserver; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.SystemClock; -import androidx.annotation.DrawableRes; -import androidx.core.os.ParcelableCompat; -import androidx.core.os.ParcelableCompatCreatorCallbacks; -import androidx.core.view.AccessibilityDelegateCompat; -import androidx.core.view.MotionEventCompat; -import androidx.core.view.VelocityTrackerCompat; -import androidx.core.view.ViewCompat; -import androidx.core.view.ViewConfigurationCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.core.view.accessibility.AccessibilityRecordCompat; -import androidx.core.widget.EdgeEffectCompat; -import android.util.AttributeSet; -import android.util.Log; -import android.view.FocusFinder; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.SoundEffectConstants; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; -import android.view.animation.Interpolator; -import android.widget.Scroller; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; - -/** - * Layout manager that allows the user to flip left and right - * through pages of data. You supply an implementation of a - * {@link PagerAdapter} to generate the pages that the view shows. - * - *

Note this class is currently under early design and - * development. The API will likely change in later updates of - * the compatibility library, requiring changes to the source code - * of apps when they are compiled against the newer version.

- * - *

ViewPager is most often used in conjunction with {@link android.app.Fragment}, - * which is a convenient way to supply and manage the lifecycle of each page. - * There are standard adapters implemented for using fragments with the ViewPager, - * which cover the most common use cases. These are - * {@link android.support.v4n.app.FragmentPagerAdapter} and - * {@link androidx.core.app.FragmentStatePagerAdapter}; each of these - * classes have simple code showing how to build a full user interface - * with them. - * - *

For more information about how to use ViewPager, read Creating Swipe Views with - * Tabs.

- * - *

Below is a more complicated example of ViewPager, using it in conjunction - * with {@link android.app.ActionBar} tabs. You can find other examples of using - * ViewPager in the API 4+ Support Demos and API 13+ Support Demos sample code. - * - * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/ActionBarTabsPager.java - * complete} - */ -public class ViewPager extends ViewGroup { - private static final String TAG = "ViewPager"; - private static final boolean DEBUG = false; - - private static final boolean USE_CACHE = false; - - private static final int DEFAULT_OFFSCREEN_PAGES = 1; - private static final int MAX_SETTLE_DURATION = 600; // ms - private static final int MIN_DISTANCE_FOR_FLING = 25; // dips - - private static final int DEFAULT_GUTTER_SIZE = 16; // dips - - private static final int MIN_FLING_VELOCITY = 400; // dips - - private static final int[] LAYOUT_ATTRS = new int[] { - android.R.attr.layout_gravity - }; - - /** - * Used to track what the expected number of items in the adapter should be. - * If the app changes this when we don't expect it, we'll throw a big obnoxious exception. - */ - private int mExpectedAdapterCount; - - static class ItemInfo { - Object object; - int position; - boolean scrolling; - float widthFactor; - float offset; - } - - private static final Comparator COMPARATOR = new Comparator(){ - @Override - public int compare(ItemInfo lhs, ItemInfo rhs) { - return lhs.position - rhs.position; - } - }; - - private static final Interpolator sInterpolator = new Interpolator() { - public float getInterpolation(float t) { - t -= 1.0f; - return t * t * t * t * t + 1.0f; - } - }; - - private final ArrayList mItems = new ArrayList(); - private final ItemInfo mTempItem = new ItemInfo(); - - private final Rect mTempRect = new Rect(); - - private PagerAdapter mAdapter; - private int mCurItem; // Index of currently displayed page. - private int mRestoredCurItem = -1; - private Parcelable mRestoredAdapterState = null; - private ClassLoader mRestoredClassLoader = null; - private Scroller mScroller; - private PagerObserver mObserver; - - private int mPageMargin; - private Drawable mMarginDrawable; - private int mTopPageBounds; - private int mBottomPageBounds; - - // Offsets of the first and last items, if known. - // Set during population, used to determine if we are at the beginning - // or end of the pager data set during touch scrolling. - private float mFirstOffset = -Float.MAX_VALUE; - private float mLastOffset = Float.MAX_VALUE; - - private int mChildWidthMeasureSpec; - private int mChildHeightMeasureSpec; - private boolean mInLayout; - - private boolean mScrollingCacheEnabled; - - private boolean mPopulatePending; - private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES; - - private boolean mIsBeingDragged; - private boolean mIsUnableToDrag; - private boolean mIgnoreGutter; - private int mDefaultGutterSize; - private int mGutterSize; - private int mTouchSlop; - /** - * Position of the last motion event. - */ - private float mLastMotionX; - private float mLastMotionY; - private float mInitialMotionX; - private float mInitialMotionY; - /** - * ID of the active pointer. This is used to retain consistency during - * drags/flings if multiple pointers are used. - */ - private int mActivePointerId = INVALID_POINTER; - /** - * Sentinel value for no current active pointer. - * Used by {@link #mActivePointerId}. - */ - private static final int INVALID_POINTER = -1; - - /** - * Determines speed during touch scrolling - */ - private VelocityTracker mVelocityTracker; - private int mMinimumVelocity; - private int mMaximumVelocity; - private int mFlingDistance; - private int mCloseEnough; - - // If the pager is at least this close to its final position, complete the scroll - // on touch down and let the user interact with the content inside instead of - // "catching" the flinging pager. - private static final int CLOSE_ENOUGH = 2; // dp - - private boolean mFakeDragging; - private long mFakeDragBeginTime; - - private EdgeEffectCompat mLeftEdge; - private EdgeEffectCompat mRightEdge; - - private boolean mFirstLayout = true; - private boolean mNeedCalculatePageOffsets = false; - private boolean mCalledSuper; - private int mDecorChildCount; - - private OnPageChangeListener mOnPageChangeListener; - private OnPageChangeListener mInternalPageChangeListener; - private OnAdapterChangeListener mAdapterChangeListener; - private PageTransformer mPageTransformer; - private Method mSetChildrenDrawingOrderEnabled; - - private static final int DRAW_ORDER_DEFAULT = 0; - private static final int DRAW_ORDER_FORWARD = 1; - private static final int DRAW_ORDER_REVERSE = 2; - private int mDrawingOrder; - private ArrayList mDrawingOrderedChildren; - private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator(); - - /** - * Indicates that the pager is in an idle, settled state. The current page - * is fully in view and no animation is in progress. - */ - public static final int SCROLL_STATE_IDLE = 0; - - /** - * Indicates that the pager is currently being dragged by the user. - */ - public static final int SCROLL_STATE_DRAGGING = 1; - - /** - * Indicates that the pager is in the process of settling to a final position. - */ - public static final int SCROLL_STATE_SETTLING = 2; - - private final Runnable mEndScrollRunnable = new Runnable() { - public void run() { - setScrollState(SCROLL_STATE_IDLE); - populate(); - } - }; - - private int mScrollState = SCROLL_STATE_IDLE; - - /** - * Callback interface for responding to changing state of the selected page. - */ - public interface OnPageChangeListener { - - /** - * This method will be invoked when the current page is scrolled, either as part - * of a programmatically initiated smooth scroll or a user initiated touch scroll. - * - * @param position Position index of the first page currently being displayed. - * Page position+1 will be visible if positionOffset is nonzero. - * @param positionOffset Value from [0, 1) indicating the offset from the page at position. - * @param positionOffsetPixels Value in pixels indicating the offset from position. - */ - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); - - /** - * This method will be invoked when a new page becomes selected. Animation is not - * necessarily complete. - * - * @param position Position index of the new selected page. - */ - public void onPageSelected(int position); - - /** - * Called when the scroll state changes. Useful for discovering when the user - * begins dragging, when the pager is automatically settling to the current page, - * or when it is fully stopped/idle. - * - * @param state The new scroll state. - * @see ViewPager#SCROLL_STATE_IDLE - * @see ViewPager#SCROLL_STATE_DRAGGING - * @see ViewPager#SCROLL_STATE_SETTLING - */ - public void onPageScrollStateChanged(int state); - } - - /** - * Simple implementation of the {@link OnPageChangeListener} interface with stub - * implementations of each method. Extend this if you do not intend to override - * every method of {@link OnPageChangeListener}. - */ - public static class SimpleOnPageChangeListener implements OnPageChangeListener { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - // This space for rent - } - - @Override - public void onPageSelected(int position) { - // This space for rent - } - - @Override - public void onPageScrollStateChanged(int state) { - // This space for rent - } - } - - /** - * A PageTransformer is invoked whenever a visible/attached page is scrolled. - * This offers an opportunity for the application to apply a custom transformation - * to the page views using animation properties. - * - *

As property animation is only supported as of Android 3.0 and forward, - * setting a PageTransformer on a ViewPager on earlier platform versions will - * be ignored.

- */ - public interface PageTransformer { - /** - * Apply a property transformation to the given page. - * - * @param page Apply the transformation to this page - * @param position Position of page relative to the current front-and-center - * position of the pager. 0 is front and center. 1 is one full - * page position to the right, and -1 is one page position to the left. - */ - public void transformPage(View page, float position); - } - - /** - * Used internally to monitor when adapters are switched. - */ - interface OnAdapterChangeListener { - public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter); - } - - /** - * Used internally to tag special types of child views that should be added as - * pager decorations by default. - */ - interface Decor {} - - public ViewPager(Context context) { - super(context); - initViewPager(); - } - - public ViewPager(Context context, AttributeSet attrs) { - super(context, attrs); - initViewPager(); - } - - void initViewPager() { - setWillNotDraw(false); - setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); - setFocusable(true); - final Context context = getContext(); - mScroller = new Scroller(context, sInterpolator); - final ViewConfiguration configuration = ViewConfiguration.get(context); - final float density = context.getResources().getDisplayMetrics().density; - - mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); - mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density); - mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); - mLeftEdge = new EdgeEffectCompat(context); - mRightEdge = new EdgeEffectCompat(context); - - mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density); - mCloseEnough = (int) (CLOSE_ENOUGH * density); - mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density); - - ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate()); - - if (ViewCompat.getImportantForAccessibility(this) - == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - ViewCompat.setImportantForAccessibility(this, - ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - } - - @Override - protected void onDetachedFromWindow() { - removeCallbacks(mEndScrollRunnable); - super.onDetachedFromWindow(); - } - - private void setScrollState(int newState) { - if (mScrollState == newState) { - return; - } - - mScrollState = newState; - if (mPageTransformer != null) { - // PageTransformers can do complex things that benefit from hardware layers. - enableLayers(newState != SCROLL_STATE_IDLE); - } - if (mOnPageChangeListener != null) { - mOnPageChangeListener.onPageScrollStateChanged(newState); - } - } - - /** - * Set a PagerAdapter that will supply views for this pager as needed. - * - * @param adapter Adapter to use - */ - public void setAdapter(PagerAdapter adapter) { - if (mAdapter != null) { - mAdapter.unregisterDataSetObserver(mObserver); - mAdapter.startUpdate(this); - for (int i = 0; i < mItems.size(); i++) { - final ItemInfo ii = mItems.get(i); - mAdapter.destroyItem(this, ii.position, ii.object); - } - mAdapter.finishUpdate(this); - mItems.clear(); - removeNonDecorViews(); - mCurItem = 0; - scrollTo(0, 0); - } - - final PagerAdapter oldAdapter = mAdapter; - mAdapter = adapter; - mExpectedAdapterCount = 0; - - if (mAdapter != null) { - if (mObserver == null) { - mObserver = new PagerObserver(); - } - mAdapter.registerDataSetObserver(mObserver); - mPopulatePending = false; - final boolean wasFirstLayout = mFirstLayout; - mFirstLayout = true; - mExpectedAdapterCount = mAdapter.getCount(); - if (mRestoredCurItem >= 0) { - mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader); - setCurrentItemInternal(mRestoredCurItem, false, true); - mRestoredCurItem = -1; - mRestoredAdapterState = null; - mRestoredClassLoader = null; - } else if (!wasFirstLayout) { - populate(); - } else { - requestLayout(); - } - } - - if (mAdapterChangeListener != null && oldAdapter != adapter) { - mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter); - } - } - - private void removeNonDecorViews() { - for (int i = 0; i < getChildCount(); i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (!lp.isDecor) { - removeViewAt(i); - i--; - } - } - } - - /** - * Retrieve the current adapter supplying pages. - * - * @return The currently registered PagerAdapter - */ - public PagerAdapter getAdapter() { - return mAdapter; - } - - void setOnAdapterChangeListener(OnAdapterChangeListener listener) { - mAdapterChangeListener = listener; - } - - private int getClientWidth() { - return getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); - } - - /** - * Set the currently selected page. If the ViewPager has already been through its first - * layout with its current adapter there will be a smooth animated transition between - * the current item and the specified item. - * - * @param item Item index to select - */ - public void setCurrentItem(int item) { - mPopulatePending = false; - setCurrentItemInternal(item, !mFirstLayout, false); - } - - /** - * Set the currently selected page. - * - * @param item Item index to select - * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately - */ - public void setCurrentItem(int item, boolean smoothScroll) { - mPopulatePending = false; - setCurrentItemInternal(item, smoothScroll, false); - } - - public int getCurrentItem() { - return mCurItem; - } - - void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) { - setCurrentItemInternal(item, smoothScroll, always, 0); - } - - void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) { - if (mAdapter == null || mAdapter.getCount() <= 0) { - setScrollingCacheEnabled(false); - return; - } - if (!always && mCurItem == item && mItems.size() != 0) { - setScrollingCacheEnabled(false); - return; - } - - if (item < 0) { - item = 0; - } else if (item >= mAdapter.getCount()) { - item = mAdapter.getCount() - 1; - } - final int pageLimit = mOffscreenPageLimit; - if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) { - // We are doing a jump by more than one page. To avoid - // glitches, we want to keep all current pages in the view - // until the scroll ends. - for (int i=0; iNote: Prior to Android 3.0 the property animation APIs did not exist. - * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.

- * - * @param reverseDrawingOrder true if the supplied PageTransformer requires page views - * to be drawn from last to first instead of first to last. - * @param transformer PageTransformer that will modify each page's animation properties - */ - public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) { - if (Build.VERSION.SDK_INT >= 11) { - final boolean hasTransformer = transformer != null; - final boolean needsPopulate = hasTransformer != (mPageTransformer != null); - mPageTransformer = transformer; - setChildrenDrawingOrderEnabledCompat(hasTransformer); - if (hasTransformer) { - mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD; - } else { - mDrawingOrder = DRAW_ORDER_DEFAULT; - } - if (needsPopulate) populate(); - } - } - - void setChildrenDrawingOrderEnabledCompat(boolean enable) { - if (Build.VERSION.SDK_INT >= 7) { - if (mSetChildrenDrawingOrderEnabled == null) { - try { - mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod( - "setChildrenDrawingOrderEnabled", new Class[] { Boolean.TYPE }); - } catch (NoSuchMethodException e) { - Log.e(TAG, "Can't find setChildrenDrawingOrderEnabled", e); - } - } - try { - mSetChildrenDrawingOrderEnabled.invoke(this, enable); - } catch (Exception e) { - Log.e(TAG, "Error changing children drawing order", e); - } - } - } - - @Override - protected int getChildDrawingOrder(int childCount, int i) { - final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i; - final int result = ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex; - return result; - } - - /** - * Set a separate OnPageChangeListener for internal use by the support library. - * - * @param listener Listener to set - * @return The old listener that was set, if any. - */ - OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) { - OnPageChangeListener oldListener = mInternalPageChangeListener; - mInternalPageChangeListener = listener; - return oldListener; - } - - /** - * Returns the number of pages that will be retained to either side of the - * current page in the view hierarchy in an idle state. Defaults to 1. - * - * @return How many pages will be kept offscreen on either side - * @see #setOffscreenPageLimit(int) - */ - public int getOffscreenPageLimit() { - return mOffscreenPageLimit; - } - - /** - * Set the number of pages that should be retained to either side of the - * current page in the view hierarchy in an idle state. Pages beyond this - * limit will be recreated from the adapter when needed. - * - *

This is offered as an optimization. If you know in advance the number - * of pages you will need to support or have lazy-loading mechanisms in place - * on your pages, tweaking this setting can have benefits in perceived smoothness - * of paging animations and interaction. If you have a small number of pages (3-4) - * that you can keep active all at once, less time will be spent in layout for - * newly created view subtrees as the user pages back and forth.

- * - *

You should keep this limit low, especially if your pages have complex layouts. - * This setting defaults to 1.

- * - * @param limit How many pages will be kept offscreen in an idle state. - */ - public void setOffscreenPageLimit(int limit) { - if (limit < DEFAULT_OFFSCREEN_PAGES) { - Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + - DEFAULT_OFFSCREEN_PAGES); - limit = DEFAULT_OFFSCREEN_PAGES; - } - if (limit != mOffscreenPageLimit) { - mOffscreenPageLimit = limit; - populate(); - } - } - - /** - * Set the margin between pages. - * - * @param marginPixels Distance between adjacent pages in pixels - * @see #getPageMargin() - * @see #setPageMarginDrawable(Drawable) - * @see #setPageMarginDrawable(int) - */ - public void setPageMargin(int marginPixels) { - final int oldMargin = mPageMargin; - mPageMargin = marginPixels; - - final int width = getWidth(); - recomputeScrollPosition(width, width, marginPixels, oldMargin); - - requestLayout(); - } - - /** - * Return the margin between pages. - * - * @return The size of the margin in pixels - */ - public int getPageMargin() { - return mPageMargin; - } - - /** - * Set a drawable that will be used to fill the margin between pages. - * - * @param d Drawable to display between pages - */ - public void setPageMarginDrawable(Drawable d) { - mMarginDrawable = d; - if (d != null) refreshDrawableState(); - setWillNotDraw(d == null); - invalidate(); - } - - /** - * Set a drawable that will be used to fill the margin between pages. - * - * @param resId Resource ID of a drawable to display between pages - */ - public void setPageMarginDrawable(@DrawableRes int resId) { - setPageMarginDrawable(getContext().getResources().getDrawable(resId)); - } - - @Override - protected boolean verifyDrawable(Drawable who) { - return super.verifyDrawable(who) || who == mMarginDrawable; - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - final Drawable d = mMarginDrawable; - if (d != null && d.isStateful()) { - d.setState(getDrawableState()); - } - } - - // We want the duration of the page snap animation to be influenced by the distance that - // the screen has to travel, however, we don't want this duration to be effected in a - // purely linear fashion. Instead, we use this method to moderate the effect that the distance - // of travel has on the overall snap duration. - float distanceInfluenceForSnapDuration(float f) { - f -= 0.5f; // center the values about 0. - f *= 0.3f * Math.PI / 2.0f; - return (float) Math.sin(f); - } - - /** - * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. - * - * @param x the number of pixels to scroll by on the X axis - * @param y the number of pixels to scroll by on the Y axis - */ - void smoothScrollTo(int x, int y) { - smoothScrollTo(x, y, 0); - } - - /** - * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. - * - * @param x the number of pixels to scroll by on the X axis - * @param y the number of pixels to scroll by on the Y axis - * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) - */ - void smoothScrollTo(int x, int y, int velocity) { - if (getChildCount() == 0) { - // Nothing to do. - setScrollingCacheEnabled(false); - return; - } - int sx = getScrollX(); - int sy = getScrollY(); - int dx = x - sx; - int dy = y - sy; - if (dx == 0 && dy == 0) { - completeScroll(false); - populate(); - setScrollState(SCROLL_STATE_IDLE); - return; - } - - setScrollingCacheEnabled(true); - setScrollState(SCROLL_STATE_SETTLING); - - final int width = getClientWidth(); - final int halfWidth = width / 2; - final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width); - final float distance = halfWidth + halfWidth * - distanceInfluenceForSnapDuration(distanceRatio); - - int duration = 0; - velocity = Math.abs(velocity); - if (velocity > 0) { - duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); - } else { - final float pageWidth = width * mAdapter.getPageWidth(mCurItem); - final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin); - duration = (int) ((pageDelta + 1) * 100); - } - duration = Math.min(duration, MAX_SETTLE_DURATION); - - mScroller.startScroll(sx, sy, dx, dy, duration); - ViewCompat.postInvalidateOnAnimation(this); - } - - ItemInfo addNewItem(int position, int index) { - ItemInfo ii = new ItemInfo(); - ii.position = position; - ii.object = mAdapter.instantiateItem(this, position); - ii.widthFactor = mAdapter.getPageWidth(position); - if (index < 0 || index >= mItems.size()) { - mItems.add(ii); - } else { - mItems.add(index, ii); - } - return ii; - } - - void dataSetChanged() { - // This method only gets called if our observer is attached, so mAdapter is non-null. - - final int adapterCount = mAdapter.getCount(); - mExpectedAdapterCount = adapterCount; - boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 && - mItems.size() < adapterCount; - int newCurrItem = mCurItem; - - boolean isUpdating = false; - for (int i = 0; i < mItems.size(); i++) { - final ItemInfo ii = mItems.get(i); - final int newPos = mAdapter.getItemPosition(ii.object); - - if (newPos == PagerAdapter.POSITION_UNCHANGED) { - continue; - } - - if (newPos == PagerAdapter.POSITION_NONE) { - mItems.remove(i); - i--; - - if (!isUpdating) { - mAdapter.startUpdate(this); - isUpdating = true; - } - - mAdapter.destroyItem(this, ii.position, ii.object); - needPopulate = true; - - if (mCurItem == ii.position) { - // Keep the current item in the valid range - newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1)); - needPopulate = true; - } - continue; - } - - if (ii.position != newPos) { - if (ii.position == mCurItem) { - // Our current item changed position. Follow it. - newCurrItem = newPos; - } - - ii.position = newPos; - needPopulate = true; - } - } - - if (isUpdating) { - mAdapter.finishUpdate(this); - } - - Collections.sort(mItems, COMPARATOR); - - if (needPopulate) { - // Reset our known page widths; populate will recompute them. - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (!lp.isDecor) { - lp.widthFactor = 0.f; - } - } - - setCurrentItemInternal(newCurrItem, false, true); - requestLayout(); - } - } - - void populate() { - populate(mCurItem); - } - - void populate(int newCurrentItem) { - ItemInfo oldCurInfo = null; - int focusDirection = View.FOCUS_FORWARD; - if (mCurItem != newCurrentItem) { - focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT : View.FOCUS_LEFT; - oldCurInfo = infoForPosition(mCurItem); - mCurItem = newCurrentItem; - } - - if (mAdapter == null) { - sortChildDrawingOrder(); - return; - } - - // Bail now if we are waiting to populate. This is to hold off - // on creating views from the time the user releases their finger to - // fling to a new position until we have finished the scroll to - // that position, avoiding glitches from happening at that point. - if (mPopulatePending) { - if (DEBUG) Log.i(TAG, "populate is pending, skipping for now..."); - sortChildDrawingOrder(); - return; - } - - // Also, don't populate until we are attached to a window. This is to - // avoid trying to populate before we have restored our view hierarchy - // state and conflicting with what is restored. - if (getWindowToken() == null) { - return; - } - - mAdapter.startUpdate(this); - - final int pageLimit = mOffscreenPageLimit; - final int startPos = Math.max(0, mCurItem - pageLimit); - final int N = mAdapter.getCount(); - final int endPos = Math.min(N-1, mCurItem + pageLimit); - - if (N != mExpectedAdapterCount) { - String resName; - try { - resName = getResources().getResourceName(getId()); - } catch (Resources.NotFoundException e) { - resName = Integer.toHexString(getId()); - } - throw new IllegalStateException("The application's PagerAdapter changed the adapter's" + - " contents without calling PagerAdapter#notifyDataSetChanged!" + - " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N + - " Pager id: " + resName + - " Pager class: " + getClass() + - " Problematic adapter: " + mAdapter.getClass()); - } - - // Locate the currently focused item or add it if needed. - int curIndex = -1; - ItemInfo curItem = null; - for (curIndex = 0; curIndex < mItems.size(); curIndex++) { - final ItemInfo ii = mItems.get(curIndex); - if (ii.position >= mCurItem) { - if (ii.position == mCurItem) curItem = ii; - break; - } - } - - if (curItem == null && N > 0) { - curItem = addNewItem(mCurItem, curIndex); - } - - // Fill 3x the available width or up to the number of offscreen - // pages requested to either side, whichever is larger. - // If we have no current item we have no work to do. - if (curItem != null) { - float extraWidthLeft = 0.f; - int itemIndex = curIndex - 1; - ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null; - final int clientWidth = getClientWidth(); - final float leftWidthNeeded = clientWidth <= 0 ? 0 : - 2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth; - for (int pos = mCurItem - 1; pos >= 0; pos--) { - if (extraWidthLeft >= leftWidthNeeded && pos < startPos) { - if (ii == null) { - break; - } - if (pos == ii.position && !ii.scrolling) { - mItems.remove(itemIndex); - mAdapter.destroyItem(this, pos, ii.object); - if (DEBUG) { - Log.i(TAG, "populate() - destroyItem() with pos: " + pos + - " view: " + ii.object); - } - itemIndex--; - curIndex--; - ii = itemIndex >= 0 ? mItems.get(itemIndex) : null; - } - } else if (ii != null && pos == ii.position) { - extraWidthLeft += ii.widthFactor; - itemIndex--; - ii = itemIndex >= 0 ? mItems.get(itemIndex) : null; - } else { - ii = addNewItem(pos, itemIndex + 1); - extraWidthLeft += ii.widthFactor; - curIndex++; - ii = itemIndex >= 0 ? mItems.get(itemIndex) : null; - } - } - - float extraWidthRight = curItem.widthFactor; - itemIndex = curIndex + 1; - if (extraWidthRight < 2.f) { - ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null; - final float rightWidthNeeded = clientWidth <= 0 ? 0 : - (float) getPaddingRight() / (float) clientWidth + 2.f; - for (int pos = mCurItem + 1; pos < N; pos++) { - if (extraWidthRight >= rightWidthNeeded && pos > endPos) { - if (ii == null) { - break; - } - if (pos == ii.position && !ii.scrolling) { - mItems.remove(itemIndex); - mAdapter.destroyItem(this, pos, ii.object); - if (DEBUG) { - Log.i(TAG, "populate() - destroyItem() with pos: " + pos + - " view: " + (ii.object)); - } - ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null; - } - } else if (ii != null && pos == ii.position) { - extraWidthRight += ii.widthFactor; - itemIndex++; - ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null; - } else { - ii = addNewItem(pos, itemIndex); - itemIndex++; - extraWidthRight += ii.widthFactor; - ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null; - } - } - } - - calculatePageOffsets(curItem, curIndex, oldCurInfo); - } - - if (DEBUG) { - Log.i(TAG, "Current page list:"); - for (int i=0; i(); - } else { - mDrawingOrderedChildren.clear(); - } - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - mDrawingOrderedChildren.add(child); - } - Collections.sort(mDrawingOrderedChildren, sPositionComparator); - } - } - - private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) { - final int N = mAdapter.getCount(); - final int width = getClientWidth(); - final float marginOffset = width > 0 ? (float) mPageMargin / width : 0; - // Fix up offsets for later layout. - if (oldCurInfo != null) { - final int oldCurPosition = oldCurInfo.position; - // Base offsets off of oldCurInfo. - if (oldCurPosition < curItem.position) { - int itemIndex = 0; - ItemInfo ii = null; - float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset; - for (int pos = oldCurPosition + 1; - pos <= curItem.position && itemIndex < mItems.size(); pos++) { - ii = mItems.get(itemIndex); - while (pos > ii.position && itemIndex < mItems.size() - 1) { - itemIndex++; - ii = mItems.get(itemIndex); - } - while (pos < ii.position) { - // We don't have an item populated for this, - // ask the adapter for an offset. - offset += mAdapter.getPageWidth(pos) + marginOffset; - pos++; - } - ii.offset = offset; - offset += ii.widthFactor + marginOffset; - } - } else if (oldCurPosition > curItem.position) { - int itemIndex = mItems.size() - 1; - ItemInfo ii = null; - float offset = oldCurInfo.offset; - for (int pos = oldCurPosition - 1; - pos >= curItem.position && itemIndex >= 0; pos--) { - ii = mItems.get(itemIndex); - while (pos < ii.position && itemIndex > 0) { - itemIndex--; - ii = mItems.get(itemIndex); - } - while (pos > ii.position) { - // We don't have an item populated for this, - // ask the adapter for an offset. - offset -= mAdapter.getPageWidth(pos) + marginOffset; - pos--; - } - offset -= ii.widthFactor + marginOffset; - ii.offset = offset; - } - } - } - - // Base all offsets off of curItem. - final int itemCount = mItems.size(); - float offset = curItem.offset; - int pos = curItem.position - 1; - mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE; - mLastOffset = curItem.position == N - 1 ? - curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE; - // Previous pages - for (int i = curIndex - 1; i >= 0; i--, pos--) { - final ItemInfo ii = mItems.get(i); - while (pos > ii.position) { - offset -= mAdapter.getPageWidth(pos--) + marginOffset; - } - offset -= ii.widthFactor + marginOffset; - ii.offset = offset; - if (ii.position == 0) mFirstOffset = offset; - } - offset = curItem.offset + curItem.widthFactor + marginOffset; - pos = curItem.position + 1; - // Next pages - for (int i = curIndex + 1; i < itemCount; i++, pos++) { - final ItemInfo ii = mItems.get(i); - while (pos < ii.position) { - offset += mAdapter.getPageWidth(pos++) + marginOffset; - } - if (ii.position == N - 1) { - mLastOffset = offset + ii.widthFactor - 1; - } - ii.offset = offset; - offset += ii.widthFactor + marginOffset; - } - - mNeedCalculatePageOffsets = false; - } - - /** - * This is the persistent state that is saved by ViewPager. Only needed - * if you are creating a sublass of ViewPager that must save its own - * state, in which case it should implement a subclass of this which - * contains that state. - */ - public static class SavedState extends BaseSavedState { - int position; - Parcelable adapterState; - ClassLoader loader; - - public SavedState(Parcelable superState) { - super(superState); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeInt(position); - out.writeParcelable(adapterState, flags); - } - - @Override - public String toString() { - return "FragmentPager.SavedState{" - + Integer.toHexString(System.identityHashCode(this)) - + " position=" + position + "}"; - } - - public static final Parcelable.Creator CREATOR - = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks() { - @TargetApi(24) - @Override - public SavedState createFromParcel(Parcel in, ClassLoader loader) { - return new SavedState(in, loader); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }); - - SavedState(Parcel in, ClassLoader loader) { - super(in); - if (loader == null) { - loader = getClass().getClassLoader(); - } - position = in.readInt(); - adapterState = in.readParcelable(loader); - this.loader = loader; - } - } - - @Override - public Parcelable onSaveInstanceState() { - Parcelable superState = super.onSaveInstanceState(); - SavedState ss = new SavedState(superState); - ss.position = mCurItem; - if (mAdapter != null) { - ss.adapterState = mAdapter.saveState(); - } - return ss; - } - - @Override - public void onRestoreInstanceState(Parcelable state) { - if (!(state instanceof SavedState)) { - super.onRestoreInstanceState(state); - return; - } - - SavedState ss = (SavedState)state; - super.onRestoreInstanceState(ss.getSuperState()); - - if (mAdapter != null) { - mAdapter.restoreState(ss.adapterState, ss.loader); - setCurrentItemInternal(ss.position, false, true); - } else { - mRestoredCurItem = ss.position; - mRestoredAdapterState = ss.adapterState; - mRestoredClassLoader = ss.loader; - } - } - - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { - if (!checkLayoutParams(params)) { - params = generateLayoutParams(params); - } - final LayoutParams lp = (LayoutParams) params; - lp.isDecor |= child instanceof Decor; - if (mInLayout) { - if (lp != null && lp.isDecor) { - throw new IllegalStateException("Cannot add pager decor view during layout"); - } - lp.needsMeasure = true; - addViewInLayout(child, index, params); - } else { - super.addView(child, index, params); - } - - if (USE_CACHE) { - if (child.getVisibility() != GONE) { - child.setDrawingCacheEnabled(mScrollingCacheEnabled); - } else { - child.setDrawingCacheEnabled(false); - } - } - } - - @Override - public void removeView(View view) { - if (mInLayout) { - removeViewInLayout(view); - } else { - super.removeView(view); - } - } - - ItemInfo infoForChild(View child) { - for (int i=0; i 0 && !mItems.isEmpty()) { - final int widthWithMargin = width - getPaddingLeft() - getPaddingRight() + margin; - final int oldWidthWithMargin = oldWidth - getPaddingLeft() - getPaddingRight() - + oldMargin; - final int xpos = getScrollX(); - final float pageOffset = (float) xpos / oldWidthWithMargin; - final int newOffsetPixels = (int) (pageOffset * widthWithMargin); - - scrollTo(newOffsetPixels, getScrollY()); - if (!mScroller.isFinished()) { - // We now return to your regularly scheduled scroll, already in progress. - final int newDuration = mScroller.getDuration() - mScroller.timePassed(); - ItemInfo targetInfo = infoForPosition(mCurItem); - mScroller.startScroll(newOffsetPixels, 0, - (int) (targetInfo.offset * width), 0, newDuration); - } - } else { - final ItemInfo ii = infoForPosition(mCurItem); - final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0; - final int scrollPos = (int) (scrollOffset * - (width - getPaddingLeft() - getPaddingRight())); - if (scrollPos != getScrollX()) { - completeScroll(false); - scrollTo(scrollPos, getScrollY()); - } - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - final int count = getChildCount(); - int width = r - l; - int height = b - t; - int paddingLeft = getPaddingLeft(); - int paddingTop = getPaddingTop(); - int paddingRight = getPaddingRight(); - int paddingBottom = getPaddingBottom(); - final int scrollX = getScrollX(); - - int decorCount = 0; - - // First pass - decor views. We need to do this in two passes so that - // we have the proper offsets for non-decor views later. - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() != GONE) { - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - int childLeft = 0; - int childTop = 0; - if (lp.isDecor) { - final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; - final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK; - switch (hgrav) { - default: - childLeft = paddingLeft; - break; - case Gravity.LEFT: - childLeft = paddingLeft; - paddingLeft += child.getMeasuredWidth(); - break; - case Gravity.CENTER_HORIZONTAL: - childLeft = Math.max((width - child.getMeasuredWidth()) / 2, - paddingLeft); - break; - case Gravity.RIGHT: - childLeft = width - paddingRight - child.getMeasuredWidth(); - paddingRight += child.getMeasuredWidth(); - break; - } - switch (vgrav) { - default: - childTop = paddingTop; - break; - case Gravity.TOP: - childTop = paddingTop; - paddingTop += child.getMeasuredHeight(); - break; - case Gravity.CENTER_VERTICAL: - childTop = Math.max((height - child.getMeasuredHeight()) / 2, - paddingTop); - break; - case Gravity.BOTTOM: - childTop = height - paddingBottom - child.getMeasuredHeight(); - paddingBottom += child.getMeasuredHeight(); - break; - } - childLeft += scrollX; - child.layout(childLeft, childTop, - childLeft + child.getMeasuredWidth(), - childTop + child.getMeasuredHeight()); - decorCount++; - } - } - } - - final int childWidth = width - paddingLeft - paddingRight; - // Page views. Do this once we have the right padding offsets from above. - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() != GONE) { - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - ItemInfo ii; - if (!lp.isDecor && (ii = infoForChild(child)) != null) { - int loff = (int) (childWidth * ii.offset); - int childLeft = paddingLeft + loff; - int childTop = paddingTop; - if (lp.needsMeasure) { - // This was added during layout and needs measurement. - // Do it now that we know what we're working with. - lp.needsMeasure = false; - final int widthSpec = MeasureSpec.makeMeasureSpec( - (int) (childWidth * lp.widthFactor), - MeasureSpec.EXACTLY); - final int heightSpec = MeasureSpec.makeMeasureSpec( - (int) (height - paddingTop - paddingBottom), - MeasureSpec.EXACTLY); - child.measure(widthSpec, heightSpec); - } - int id = -1; - - if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object - + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth() - + "x" + child.getMeasuredHeight()); - - child.layout(childLeft, childTop, - childLeft + child.getMeasuredWidth(), - childTop + child.getMeasuredHeight()); - } - } - } - mTopPageBounds = paddingTop; - mBottomPageBounds = height - paddingBottom; - mDecorChildCount = decorCount; - - if (mFirstLayout) { - scrollToItem(mCurItem, false, 0, false); - } - mFirstLayout = false; - } - - @Override - public void computeScroll() { - if (!mScroller.isFinished() && mScroller.computeScrollOffset()) { - int oldX = getScrollX(); - int oldY = getScrollY(); - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - - if (oldX != x || oldY != y) { - scrollTo(x, y); - if (!pageScrolled(x)) { - mScroller.abortAnimation(); - scrollTo(0, y); - } - } - - // Keep on drawing until the animation has finished. - ViewCompat.postInvalidateOnAnimation(this); - return; - } - - // Done with scroll, clean up state. - completeScroll(true); - } - - private boolean pageScrolled(int xpos) { - if (mItems.size() == 0) { - mCalledSuper = false; - onPageScrolled(0, 0, 0); - if (!mCalledSuper) { - throw new IllegalStateException( - "onPageScrolled did not call superclass implementation"); - } - return false; - } - final ItemInfo ii = infoForCurrentScrollPosition(); - final int width = getClientWidth(); - final int widthWithMargin = width + mPageMargin; - final float marginOffset = (float) mPageMargin / width; - final int currentPage = ii.position; - final float pageOffset = (((float) xpos / width) - ii.offset) / - (ii.widthFactor + marginOffset); - final int offsetPixels = (int) (pageOffset * widthWithMargin); - - mCalledSuper = false; - onPageScrolled(currentPage, pageOffset, offsetPixels); - if (!mCalledSuper) { - throw new IllegalStateException( - "onPageScrolled did not call superclass implementation"); - } - return true; - } - - /** - * This method will be invoked when the current page is scrolled, either as part - * of a programmatically initiated smooth scroll or a user initiated touch scroll. - * If you override this method you must call through to the superclass implementation - * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled - * returns. - * - * @param position Position index of the first page currently being displayed. - * Page position+1 will be visible if positionOffset is nonzero. - * @param offset Value from [0, 1) indicating the offset from the page at position. - * @param offsetPixels Value in pixels indicating the offset from position. - */ - protected void onPageScrolled(int position, float offset, int offsetPixels) { - // Offset any decor views if needed - keep them on-screen at all times. - if (mDecorChildCount > 0) { - final int scrollX = getScrollX(); - int paddingLeft = getPaddingLeft(); - int paddingRight = getPaddingRight(); - final int width = getWidth(); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (!lp.isDecor) continue; - - final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; - int childLeft = 0; - switch (hgrav) { - default: - childLeft = paddingLeft; - break; - case Gravity.LEFT: - childLeft = paddingLeft; - paddingLeft += child.getWidth(); - break; - case Gravity.CENTER_HORIZONTAL: - childLeft = Math.max((width - child.getMeasuredWidth()) / 2, - paddingLeft); - break; - case Gravity.RIGHT: - childLeft = width - paddingRight - child.getMeasuredWidth(); - paddingRight += child.getMeasuredWidth(); - break; - } - childLeft += scrollX; - - final int childOffset = childLeft - child.getLeft(); - if (childOffset != 0) { - child.offsetLeftAndRight(childOffset); - } - } - } - - if (mOnPageChangeListener != null) { - mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels); - } - if (mInternalPageChangeListener != null) { - mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels); - } - - if (mPageTransformer != null) { - final int scrollX = getScrollX(); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - if (lp.isDecor) continue; - - final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth(); - mPageTransformer.transformPage(child, transformPos); - } - } - - mCalledSuper = true; - } - - private void completeScroll(boolean postEvents) { - boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING; - if (needPopulate) { - // Done with scroll, no longer want to cache view drawing. - setScrollingCacheEnabled(false); - mScroller.abortAnimation(); - int oldX = getScrollX(); - int oldY = getScrollY(); - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - if (oldX != x || oldY != y) { - scrollTo(x, y); - } - } - mPopulatePending = false; - for (int i=0; i 0) || (x > getWidth() - mGutterSize && dx < 0); - } - - @SuppressLint("WrongConstant") - private void enableLayers(boolean enable) { - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final int layerType = enable ? - View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE; - getChildAt(i).setLayerType(layerType, null); - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - /* - * This method JUST determines whether we want to intercept the motion. - * If we return true, onMotionEvent will be called and we do the actual - * scrolling there. - */ - - final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; - - // Always take care of the touch gesture being complete. - if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { - // Release the drag. - if (DEBUG) Log.v(TAG, "Intercept done!"); - mIsBeingDragged = false; - mIsUnableToDrag = false; - mActivePointerId = INVALID_POINTER; - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - return false; - } - - // Nothing more to do here if we have decided whether or not we - // are dragging. - if (action != MotionEvent.ACTION_DOWN) { - if (mIsBeingDragged) { - if (DEBUG) Log.v(TAG, "Intercept returning true!"); - return true; - } - if (mIsUnableToDrag) { - if (DEBUG) Log.v(TAG, "Intercept returning false!"); - return false; - } - } - - switch (action) { - case MotionEvent.ACTION_MOVE: { - /* - * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. - */ - - /* - * Locally do absolute value. mLastMotionY is set to the y value - * of the down event. - */ - final int activePointerId = mActivePointerId; - if (activePointerId == INVALID_POINTER) { - // If we don't have a valid id, the touch down wasn't on content. - break; - } - - final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId); - final float x = MotionEventCompat.getX(ev, pointerIndex); - final float dx = x - mLastMotionX; - final float xDiff = Math.abs(dx); - final float y = MotionEventCompat.getY(ev, pointerIndex); - final float yDiff = Math.abs(y - mInitialMotionY); - if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); - - if (dx != 0 && !isGutterDrag(mLastMotionX, dx) && - canScroll(this, false, (int) dx, (int) x, (int) y)) { - // Nested view has scrollable area under this point. Let it be handled there. - mLastMotionX = x; - mLastMotionY = y; - mIsUnableToDrag = true; - return false; - } - if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) { - if (DEBUG) Log.v(TAG, "Starting drag!"); - mIsBeingDragged = true; - requestParentDisallowInterceptTouchEvent(true); - setScrollState(SCROLL_STATE_DRAGGING); - mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop : - mInitialMotionX - mTouchSlop; - mLastMotionY = y; - setScrollingCacheEnabled(true); - } else if (yDiff > mTouchSlop) { - // The finger has moved enough in the vertical - // direction to be counted as a drag... abort - // any attempt to drag horizontally, to work correctly - // with children that have scrolling containers. - if (DEBUG) Log.v(TAG, "Starting unable to drag!"); - mIsUnableToDrag = true; - } - if (mIsBeingDragged) { - // Scroll to follow the motion event - if (performDrag(x)) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - break; - } - - case MotionEvent.ACTION_DOWN: { - /* - * Remember location of down touch. - * ACTION_DOWN always refers to pointer index 0. - */ - mLastMotionX = mInitialMotionX = ev.getX(); - mLastMotionY = mInitialMotionY = ev.getY(); - mActivePointerId = MotionEventCompat.getPointerId(ev, 0); - mIsUnableToDrag = false; - - mScroller.computeScrollOffset(); - if (mScrollState == SCROLL_STATE_SETTLING && - Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) { - // Let the user 'catch' the pager as it animates. - mScroller.abortAnimation(); - mPopulatePending = false; - populate(); - mIsBeingDragged = true; - requestParentDisallowInterceptTouchEvent(true); - setScrollState(SCROLL_STATE_DRAGGING); - } else { - completeScroll(false); - mIsBeingDragged = false; - } - - if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY - + " mIsBeingDragged=" + mIsBeingDragged - + "mIsUnableToDrag=" + mIsUnableToDrag); - break; - } - - case MotionEventCompat.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - break; - } - - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - - /* - * The only time we want to intercept motion events is if we are in the - * drag mode. - */ - return mIsBeingDragged; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (mFakeDragging) { - // A fake drag is in progress already, ignore this real one - // but still eat the touch events. - // (It is likely that the user is multi-touching the screen.) - return true; - } - - if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { - // Don't handle edge touches immediately -- they may actually belong to one of our - // descendants. - return false; - } - - if (mAdapter == null || mAdapter.getCount() == 0) { - // Nothing to present or scroll; nothing to touch. - return false; - } - - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - - final int action = ev.getAction(); - boolean needsInvalidate = false; - - switch (action & MotionEventCompat.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: { - mScroller.abortAnimation(); - mPopulatePending = false; - populate(); - - // Remember where the motion event started - mLastMotionX = mInitialMotionX = ev.getX(); - mLastMotionY = mInitialMotionY = ev.getY(); - mActivePointerId = MotionEventCompat.getPointerId(ev, 0); - break; - } - case MotionEvent.ACTION_MOVE: - if (!mIsBeingDragged) { - final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); - final float x = MotionEventCompat.getX(ev, pointerIndex); - final float xDiff = Math.abs(x - mLastMotionX); - final float y = MotionEventCompat.getY(ev, pointerIndex); - final float yDiff = Math.abs(y - mLastMotionY); - if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); - if (xDiff > mTouchSlop && xDiff > yDiff) { - if (DEBUG) Log.v(TAG, "Starting drag!"); - mIsBeingDragged = true; - requestParentDisallowInterceptTouchEvent(true); - mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop : - mInitialMotionX - mTouchSlop; - mLastMotionY = y; - setScrollState(SCROLL_STATE_DRAGGING); - setScrollingCacheEnabled(true); - - // Disallow Parent Intercept, just in case - ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - } - } - // Not else! Note that mIsBeingDragged can be set above. - if (mIsBeingDragged) { - // Scroll to follow the motion event - final int activePointerIndex = MotionEventCompat.findPointerIndex( - ev, mActivePointerId); - final float x = MotionEventCompat.getX(ev, activePointerIndex); - needsInvalidate |= performDrag(x); - } - break; - case MotionEvent.ACTION_UP: - if (mIsBeingDragged) { - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( - velocityTracker, mActivePointerId); - mPopulatePending = true; - final int width = getClientWidth(); - final int scrollX = getScrollX(); - final ItemInfo ii = infoForCurrentScrollPosition(); - final int currentPage = ii.position; - final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor; - final int activePointerIndex = - MotionEventCompat.findPointerIndex(ev, mActivePointerId); - final float x = MotionEventCompat.getX(ev, activePointerIndex); - final int totalDelta = (int) (x - mInitialMotionX); - int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity, - totalDelta); - setCurrentItemInternal(nextPage, true, true, initialVelocity); - - mActivePointerId = INVALID_POINTER; - endDrag(); - needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); - } - break; - case MotionEvent.ACTION_CANCEL: - if (mIsBeingDragged) { - scrollToItem(mCurItem, true, 0, false); - mActivePointerId = INVALID_POINTER; - endDrag(); - needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); - } - break; - case MotionEventCompat.ACTION_POINTER_DOWN: { - final int index = MotionEventCompat.getActionIndex(ev); - final float x = MotionEventCompat.getX(ev, index); - mLastMotionX = x; - mActivePointerId = MotionEventCompat.getPointerId(ev, index); - break; - } - case MotionEventCompat.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - mLastMotionX = MotionEventCompat.getX(ev, - MotionEventCompat.findPointerIndex(ev, mActivePointerId)); - break; - } - if (needsInvalidate) { - ViewCompat.postInvalidateOnAnimation(this); - } - return true; - } - - private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) { - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(disallowIntercept); - } - } - - private boolean performDrag(float x) { - boolean needsInvalidate = false; - - final float deltaX = mLastMotionX - x; - mLastMotionX = x; - - float oldScrollX = getScrollX(); - float scrollX = oldScrollX + deltaX; - final int width = getClientWidth(); - - float leftBound = width * mFirstOffset; - float rightBound = width * mLastOffset; - boolean leftAbsolute = true; - boolean rightAbsolute = true; - - final ItemInfo firstItem = mItems.get(0); - final ItemInfo lastItem = mItems.get(mItems.size() - 1); - if (firstItem.position != 0) { - leftAbsolute = false; - leftBound = firstItem.offset * width; - } - if (lastItem.position != mAdapter.getCount() - 1) { - rightAbsolute = false; - rightBound = lastItem.offset * width; - } - - if (scrollX < leftBound) { - if (leftAbsolute) { - float over = leftBound - scrollX; - needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width); - } - scrollX = leftBound; - } else if (scrollX > rightBound) { - if (rightAbsolute) { - float over = scrollX - rightBound; - needsInvalidate = mRightEdge.onPull(Math.abs(over) / width); - } - scrollX = rightBound; - } - // Don't lose the rounded component - mLastMotionX += scrollX - (int) scrollX; - scrollTo((int) scrollX, getScrollY()); - pageScrolled((int) scrollX); - - return needsInvalidate; - } - - /** - * @return Info about the page at the current scroll position. - * This can be synthetic for a missing middle page; the 'object' field can be null. - */ - private ItemInfo infoForCurrentScrollPosition() { - final int width = getClientWidth(); - final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0; - final float marginOffset = width > 0 ? (float) mPageMargin / width : 0; - int lastPos = -1; - float lastOffset = 0.f; - float lastWidth = 0.f; - boolean first = true; - - ItemInfo lastItem = null; - for (int i = 0; i < mItems.size(); i++) { - ItemInfo ii = mItems.get(i); - float offset; - if (!first && ii.position != lastPos + 1) { - // Create a synthetic item for a missing page. - ii = mTempItem; - ii.offset = lastOffset + lastWidth + marginOffset; - ii.position = lastPos + 1; - ii.widthFactor = mAdapter.getPageWidth(ii.position); - i--; - } - offset = ii.offset; - - final float leftBound = offset; - final float rightBound = offset + ii.widthFactor + marginOffset; - if (first || scrollOffset >= leftBound) { - if (scrollOffset < rightBound || i == mItems.size() - 1) { - return ii; - } - } else { - return lastItem; - } - first = false; - lastPos = ii.position; - lastOffset = offset; - lastWidth = ii.widthFactor; - lastItem = ii; - } - - return lastItem; - } - - private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) { - int targetPage; - if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) { - targetPage = velocity > 0 ? currentPage : currentPage + 1; - } else { - final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f; - targetPage = (int) (currentPage + pageOffset + truncator); - } - - if (mItems.size() > 0) { - final ItemInfo firstItem = mItems.get(0); - final ItemInfo lastItem = mItems.get(mItems.size() - 1); - - // Only let the user target pages we have items for - targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position)); - } - - return targetPage; - } - - @Override - public void draw(Canvas canvas) { - super.draw(canvas); - boolean needsInvalidate = false; - - final int overScrollMode = ViewCompat.getOverScrollMode(this); - if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS || - (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && - mAdapter != null && mAdapter.getCount() > 1)) { - if (!mLeftEdge.isFinished()) { - final int restoreCount = canvas.save(); - final int height = getHeight() - getPaddingTop() - getPaddingBottom(); - final int width = getWidth(); - - canvas.rotate(270); - canvas.translate(-height + getPaddingTop(), mFirstOffset * width); - mLeftEdge.setSize(height, width); - needsInvalidate |= mLeftEdge.draw(canvas); - canvas.restoreToCount(restoreCount); - } - if (!mRightEdge.isFinished()) { - final int restoreCount = canvas.save(); - final int width = getWidth(); - final int height = getHeight() - getPaddingTop() - getPaddingBottom(); - - canvas.rotate(90); - canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * width); - mRightEdge.setSize(height, width); - needsInvalidate |= mRightEdge.draw(canvas); - canvas.restoreToCount(restoreCount); - } - } else { - mLeftEdge.finish(); - mRightEdge.finish(); - } - - if (needsInvalidate) { - // Keep animating - ViewCompat.postInvalidateOnAnimation(this); - } - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - // Draw the margin drawable between pages if needed. - if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) { - final int scrollX = getScrollX(); - final int width = getWidth(); - - final float marginOffset = (float) mPageMargin / width; - int itemIndex = 0; - ItemInfo ii = mItems.get(0); - float offset = ii.offset; - final int itemCount = mItems.size(); - final int firstPos = ii.position; - final int lastPos = mItems.get(itemCount - 1).position; - for (int pos = firstPos; pos < lastPos; pos++) { - while (pos > ii.position && itemIndex < itemCount) { - ii = mItems.get(++itemIndex); - } - - float drawAt; - if (pos == ii.position) { - drawAt = (ii.offset + ii.widthFactor) * width; - offset = ii.offset + ii.widthFactor + marginOffset; - } else { - float widthFactor = mAdapter.getPageWidth(pos); - drawAt = (offset + widthFactor) * width; - offset += widthFactor + marginOffset; - } - - if (drawAt + mPageMargin > scrollX) { - mMarginDrawable.setBounds((int) drawAt, mTopPageBounds, - (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds); - mMarginDrawable.draw(canvas); - } - - if (drawAt > scrollX + width) { - break; // No more visible, no sense in continuing - } - } - } - } - - /** - * Start a fake drag of the pager. - * - *

A fake drag can be useful if you want to synchronize the motion of the ViewPager - * with the touch scrolling of another view, while still letting the ViewPager - * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.) - * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call - * {@link #endFakeDrag()} to complete the fake drag and fling as necessary. - * - *

During a fake drag the ViewPager will ignore all touch events. If a real drag - * is already in progress, this method will return false. - * - * @return true if the fake drag began successfully, false if it could not be started. - * - * @see #fakeDragBy(float) - * @see #endFakeDrag() - */ - public boolean beginFakeDrag() { - if (mIsBeingDragged) { - return false; - } - mFakeDragging = true; - setScrollState(SCROLL_STATE_DRAGGING); - mInitialMotionX = mLastMotionX = 0; - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } else { - mVelocityTracker.clear(); - } - final long time = SystemClock.uptimeMillis(); - final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0); - mVelocityTracker.addMovement(ev); - ev.recycle(); - mFakeDragBeginTime = time; - return true; - } - - /** - * End a fake drag of the pager. - * - * @see #beginFakeDrag() - * @see #fakeDragBy(float) - */ - public void endFakeDrag() { - if (!mFakeDragging) { - throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); - } - - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( - velocityTracker, mActivePointerId); - mPopulatePending = true; - final int width = getClientWidth(); - final int scrollX = getScrollX(); - final ItemInfo ii = infoForCurrentScrollPosition(); - final int currentPage = ii.position; - final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor; - final int totalDelta = (int) (mLastMotionX - mInitialMotionX); - int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity, - totalDelta); - setCurrentItemInternal(nextPage, true, true, initialVelocity); - endDrag(); - - mFakeDragging = false; - } - - /** - * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first. - * - * @param xOffset Offset in pixels to drag by. - * @see #beginFakeDrag() - * @see #endFakeDrag() - */ - public void fakeDragBy(float xOffset) { - if (!mFakeDragging) { - throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); - } - - mLastMotionX += xOffset; - - float oldScrollX = getScrollX(); - float scrollX = oldScrollX - xOffset; - final int width = getClientWidth(); - - float leftBound = width * mFirstOffset; - float rightBound = width * mLastOffset; - - final ItemInfo firstItem = mItems.get(0); - final ItemInfo lastItem = mItems.get(mItems.size() - 1); - if (firstItem.position != 0) { - leftBound = firstItem.offset * width; - } - if (lastItem.position != mAdapter.getCount() - 1) { - rightBound = lastItem.offset * width; - } - - if (scrollX < leftBound) { - scrollX = leftBound; - } else if (scrollX > rightBound) { - scrollX = rightBound; - } - // Don't lose the rounded component - mLastMotionX += scrollX - (int) scrollX; - scrollTo((int) scrollX, getScrollY()); - pageScrolled((int) scrollX); - - // Synthesize an event for the VelocityTracker. - final long time = SystemClock.uptimeMillis(); - final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE, - mLastMotionX, 0, 0); - mVelocityTracker.addMovement(ev); - ev.recycle(); - } - - /** - * Returns true if a fake drag is in progress. - * - * @return true if currently in a fake drag, false otherwise. - * - * @see #beginFakeDrag() - * @see #fakeDragBy(float) - * @see #endFakeDrag() - */ - public boolean isFakeDragging() { - return mFakeDragging; - } - - private void onSecondaryPointerUp(MotionEvent ev) { - final int pointerIndex = MotionEventCompat.getActionIndex(ev); - final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); - if (pointerId == mActivePointerId) { - // This was our active pointer going up. Choose a new - // active pointer and adjust accordingly. - final int newPointerIndex = pointerIndex == 0 ? 1 : 0; - mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex); - mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } - } - } - - private void endDrag() { - mIsBeingDragged = false; - mIsUnableToDrag = false; - - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - - private void setScrollingCacheEnabled(boolean enabled) { - if (mScrollingCacheEnabled != enabled) { - mScrollingCacheEnabled = enabled; - if (USE_CACHE) { - final int size = getChildCount(); - for (int i = 0; i < size; ++i) { - final View child = getChildAt(i); - if (child.getVisibility() != GONE) { - child.setDrawingCacheEnabled(enabled); - } - } - } - } - } - - public boolean canScrollHorizontally(int direction) { - if (mAdapter == null) { - return false; - } - - final int width = getClientWidth(); - final int scrollX = getScrollX(); - if (direction < 0) { - return (scrollX > (int) (width * mFirstOffset)); - } else if (direction > 0) { - return (scrollX < (int) (width * mLastOffset)); - } else { - return false; - } - } - - /** - * Tests scrollability within child views of v given a delta of dx. - * - * @param v View to test for horizontal scrollability - * @param checkV Whether the view v passed should itself be checked for scrollability (true), - * or just its children (false). - * @param dx Delta scrolled in pixels - * @param x X coordinate of the active touch point - * @param y Y coordinate of the active touch point - * @return true if child views of v can be scrolled by delta of dx. - */ - protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { - if (v instanceof ViewGroup) { - final ViewGroup group = (ViewGroup) v; - final int scrollX = v.getScrollX(); - final int scrollY = v.getScrollY(); - final int count = group.getChildCount(); - // Count backwards - let topmost views consume scroll distance first. - for (int i = count - 1; i >= 0; i--) { - // TODO: Add versioned support here for transformed views. - // This will not work for transformed views in Honeycomb+ - final View child = group.getChildAt(i); - if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && - y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && - canScroll(child, true, dx, x + scrollX - child.getLeft(), - y + scrollY - child.getTop())) { - return true; - } - } - } - - return checkV && ViewCompat.canScrollHorizontally(v, -dx); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - // Let the focused view and/or our descendants get the key first - return super.dispatchKeyEvent(event) || executeKeyEvent(event); - } - - /** - * You can call this function yourself to have the scroll view perform - * scrolling from a key event, just as if the event had been dispatched to - * it by the view hierarchy. - * - * @param event The key event to execute. - * @return Return true if the event was handled, else false. - */ - public boolean executeKeyEvent(KeyEvent event) { - boolean handled = false; - if (event.getAction() == KeyEvent.ACTION_DOWN) { - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_DPAD_LEFT: - handled = arrowScroll(FOCUS_LEFT); - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - handled = arrowScroll(FOCUS_RIGHT); - break; - } - } - return handled; - } - - public boolean arrowScroll(int direction) { - View currentFocused = findFocus(); - if (currentFocused == this) { - currentFocused = null; - } else if (currentFocused != null) { - boolean isChild = false; - for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup; - parent = parent.getParent()) { - if (parent == this) { - isChild = true; - break; - } - } - if (!isChild) { - // This would cause the focus search down below to fail in fun ways. - final StringBuilder sb = new StringBuilder(); - sb.append(currentFocused.getClass().getSimpleName()); - for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup; - parent = parent.getParent()) { - sb.append(" => ").append(parent.getClass().getSimpleName()); - } - Log.e(TAG, "arrowScroll tried to find focus based on non-child " + - "current focused view " + sb.toString()); - currentFocused = null; - } - } - - boolean handled = false; - - View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, - direction); - if (nextFocused != null && nextFocused != currentFocused) { - if (direction == View.FOCUS_LEFT) { - // If there is nothing to the left, or this is causing us to - // jump to the right, then what we really want to do is page left. - final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left; - final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left; - if (currentFocused != null && nextLeft >= currLeft) { - handled = pageLeft(); - } else { - handled = nextFocused.requestFocus(); - } - } else if (direction == View.FOCUS_RIGHT) { - // If there is nothing to the right, or this is causing us to - // jump to the left, then what we really want to do is page right. - final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left; - final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left; - if (currentFocused != null && nextLeft <= currLeft) { - handled = pageRight(); - } else { - handled = nextFocused.requestFocus(); - } - } - } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) { - // Trying to move left and nothing there; try to page. - handled = pageLeft(); - } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) { - // Trying to move right and nothing there; try to page. - handled = pageRight(); - } - if (handled) { - playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); - } - return handled; - } - - private Rect getChildRectInPagerCoordinates(Rect outRect, View child) { - if (outRect == null) { - outRect = new Rect(); - } - if (child == null) { - outRect.set(0, 0, 0, 0); - return outRect; - } - outRect.left = child.getLeft(); - outRect.right = child.getRight(); - outRect.top = child.getTop(); - outRect.bottom = child.getBottom(); - - ViewParent parent = child.getParent(); - while (parent instanceof ViewGroup && parent != this) { - final ViewGroup group = (ViewGroup) parent; - outRect.left += group.getLeft(); - outRect.right += group.getRight(); - outRect.top += group.getTop(); - outRect.bottom += group.getBottom(); - - parent = group.getParent(); - } - return outRect; - } - - boolean pageLeft() { - if (mCurItem > 0) { - setCurrentItem(mCurItem-1, true); - return true; - } - return false; - } - - boolean pageRight() { - if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) { - setCurrentItem(mCurItem+1, true); - return true; - } - return false; - } - - /** - * We only want the current page that is being shown to be focusable. - */ - @Override - public void addFocusables(ArrayList views, int direction, int focusableMode) { - final int focusableCount = views.size(); - - final int descendantFocusability = getDescendantFocusability(); - - if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { - for (int i = 0; i < getChildCount(); i++) { - final View child = getChildAt(i); - if (child.getVisibility() == VISIBLE) { - ItemInfo ii = infoForChild(child); - if (ii != null && ii.position == mCurItem) { - child.addFocusables(views, direction, focusableMode); - } - } - } - } - - // we add ourselves (if focusable) in all cases except for when we are - // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is - // to avoid the focus search finding layouts when a more precise search - // among the focusable children would be more interesting. - if ( - descendantFocusability != FOCUS_AFTER_DESCENDANTS || - // No focusable descendants - (focusableCount == views.size())) { - // Note that we can't call the superclass here, because it will - // add all views in. So we need to do the same thing View does. - if (!isFocusable()) { - return; - } - if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && - isInTouchMode() && !isFocusableInTouchMode()) { - return; - } - if (views != null) { - views.add(this); - } - } - } - - /** - * We only want the current page that is being shown to be touchable. - */ - @Override - public void addTouchables(ArrayList views) { - // Note that we don't call super.addTouchables(), which means that - // we don't call View.addTouchables(). This is okay because a ViewPager - // is itself not touchable. - for (int i = 0; i < getChildCount(); i++) { - final View child = getChildAt(i); - if (child.getVisibility() == VISIBLE) { - ItemInfo ii = infoForChild(child); - if (ii != null && ii.position == mCurItem) { - child.addTouchables(views); - } - } - } - } - - /** - * We only want the current page that is being shown to be focusable. - */ - @Override - protected boolean onRequestFocusInDescendants(int direction, - Rect previouslyFocusedRect) { - int index; - int increment; - int end; - int count = getChildCount(); - if ((direction & FOCUS_FORWARD) != 0) { - index = 0; - increment = 1; - end = count; - } else { - index = count - 1; - increment = -1; - end = -1; - } - for (int i = index; i != end; i += increment) { - View child = getChildAt(i); - if (child.getVisibility() == VISIBLE) { - ItemInfo ii = infoForChild(child); - if (ii != null && ii.position == mCurItem) { - if (child.requestFocus(direction, previouslyFocusedRect)) { - return true; - } - } - } - } - return false; - } - - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - // Dispatch scroll events from this ViewPager. - if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { - return super.dispatchPopulateAccessibilityEvent(event); - } - - // Dispatch all other accessibility events from the current page. - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == VISIBLE) { - final ItemInfo ii = infoForChild(child); - if (ii != null && ii.position == mCurItem && - child.dispatchPopulateAccessibilityEvent(event)) { - return true; - } - } - } - - return false; - } - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return generateDefaultLayoutParams(); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams && super.checkLayoutParams(p); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - class MyAccessibilityDelegate extends AccessibilityDelegateCompat { - - @Override - public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(host, event); - event.setClassName(ViewPager.class.getName()); - final AccessibilityRecordCompat recordCompat = AccessibilityRecordCompat.obtain(); - recordCompat.setScrollable(canScroll()); - if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED - && mAdapter != null) { - recordCompat.setItemCount(mAdapter.getCount()); - recordCompat.setFromIndex(mCurItem); - recordCompat.setToIndex(mCurItem); - } - } - - @Override - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { - super.onInitializeAccessibilityNodeInfo(host, info); - info.setClassName(ViewPager.class.getName()); - info.setScrollable(canScroll()); - if (canScrollHorizontally(1)) { - info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD); - } - if (canScrollHorizontally(-1)) { - info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD); - } - } - - @Override - public boolean performAccessibilityAction(View host, int action, Bundle args) { - if (super.performAccessibilityAction(host, action, args)) { - return true; - } - switch (action) { - case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: { - if (canScrollHorizontally(1)) { - setCurrentItem(mCurItem + 1); - return true; - } - } return false; - case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: { - if (canScrollHorizontally(-1)) { - setCurrentItem(mCurItem - 1); - return true; - } - } return false; - } - return false; - } - - private boolean canScroll() { - return (mAdapter != null) && (mAdapter.getCount() > 1); - } - } - - private class PagerObserver extends DataSetObserver { - @Override - public void onChanged() { - dataSetChanged(); - } - @Override - public void onInvalidated() { - dataSetChanged(); - } - } - - /** - * Layout parameters that should be supplied for views added to a - * ViewPager. - */ - public static class LayoutParams extends ViewGroup.LayoutParams { - /** - * true if this view is a decoration on the pager itself and not - * a view supplied by the adapter. - */ - public boolean isDecor; - - /** - * Gravity setting for use on decor views only: - * Where to position the view page within the overall ViewPager - * container; constants are defined in {@link android.view.Gravity}. - */ - public int gravity; - - /** - * Width as a 0-1 multiplier of the measured pager width - */ - float widthFactor = 0.f; - - /** - * true if this view was added during layout and needs to be measured - * before being positioned. - */ - boolean needsMeasure; - - /** - * Adapter position this view is for if !isDecor - */ - int position; - - /** - * Current child index within the ViewPager that this view occupies - */ - int childIndex; - - public LayoutParams() { - super(FILL_PARENT, FILL_PARENT); - } - - public LayoutParams(Context context, AttributeSet attrs) { - super(context, attrs); - - final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS); - gravity = a.getInteger(0, Gravity.TOP); - a.recycle(); - } - } - - static class ViewPositionComparator implements Comparator { - @Override - public int compare(View lhs, View rhs) { - final LayoutParams llp = (LayoutParams) lhs.getLayoutParams(); - final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams(); - if (llp.isDecor != rlp.isDecor) { - return llp.isDecor ? 1 : -1; - } - return llp.position - rlp.position; - } - } -} diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java b/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java index 7258d8d6..68dd137e 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java +++ b/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java @@ -5,21 +5,18 @@ package de.blinkt.openvpn.activities; -import android.annotation.TargetApi; -import android.app.Activity; import android.app.UiModeManager; -import android.content.Context; -import android.content.RestrictionsManager; import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; -import android.os.UserManager; import android.view.Window; -import de.blinkt.openvpn.api.AppRestrictions; -public class BaseActivity extends Activity { +import androidx.appcompat.app.AppCompatActivity; + +public abstract class BaseActivity extends AppCompatActivity { private boolean isAndroidTV() { - final UiModeManager uiModeManager = (UiModeManager) getSystemService(Activity.UI_MODE_SERVICE); + final UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE); + if (uiModeManager == null) + return false; return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION; } diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt index 672ffae6..b2a76f3d 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt @@ -154,7 +154,7 @@ class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener outState.putParcelable("mSourceUri", mSourceUri) } - override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent) { + override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) { if (requestCode == RESULT_INSTALLPKCS12 && resultCode == Activity.RESULT_OK) { showCertDialog() } @@ -593,8 +593,6 @@ class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener } - val intent = intent - if (intent != null) { doImportIntent(intent) @@ -607,6 +605,7 @@ class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener private fun doImportIntent(intent: Intent) { val data = intent.data + if (intent.action.equals(IMPORT_PROFILE_DATA)) if (data != null) { mSourceUri = data doImportUri(data) @@ -698,7 +697,7 @@ class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener mResult!!.mName = getUniqueProfileName(possibleName) mProfilename.visibility = View.VISIBLE mProfilenameLabel.visibility = View.VISIBLE - mProfilename!!.setText(mResult!!.name) + mProfilename.setText(mResult!!.name) log(R.string.import_done) } @@ -785,6 +784,7 @@ class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener companion object { + @kotlin.jvm.JvmField val IMPORT_PROFILE = "de.blinkt.openvpn.IMPORT_PROFILE" val IMPORT_PROFILE_DATA = "de.blinkt.openvpn.IMPORT_PROFILE_DATA" private val RESULT_INSTALLPKCS12 = 7 diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java b/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java index db70eca9..fa0106a9 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java +++ b/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java @@ -23,7 +23,7 @@ public class LogWindow extends BaseActivity { getActionBar().setDisplayHomeAsUpEnabled(true); if (savedInstanceState == null) { - getFragmentManager().beginTransaction() + getSupportFragmentManager().beginTransaction() .add(R.id.container, new LogFragment()) .commit(); } diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java index f7c46d01..2a6a0fff 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java +++ b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java @@ -6,19 +6,14 @@ package de.blinkt.openvpn.activities; import android.annotation.TargetApi; -import android.app.ActionBar; -import android.app.Activity; -import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Build; -import android.os.PowerManager; -import android.provider.Settings; -import android.support.v4n.view.ViewPager; import android.view.Menu; import android.view.MenuItem; +import androidx.appcompat.app.ActionBar; +import androidx.viewpager.widget.ViewPager; + import de.blinkt.openvpn.R; import de.blinkt.openvpn.fragments.AboutFragment; import de.blinkt.openvpn.fragments.FaqFragment; @@ -28,16 +23,13 @@ import de.blinkt.openvpn.fragments.LogFragment; import de.blinkt.openvpn.fragments.SendDumpFragment; import de.blinkt.openvpn.fragments.VPNProfileList; import de.blinkt.openvpn.views.ScreenSlidePagerAdapter; -import de.blinkt.openvpn.views.SlidingTabLayout; -import de.blinkt.openvpn.views.TabBarView; public class MainActivity extends BaseActivity { private ViewPager mPager; private ScreenSlidePagerAdapter mPagerAdapter; - private SlidingTabLayout mSlidingTabLayout; - private TabBarView mTabs; + //private TabLayout mTabs; protected void onCreate(android.os.Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -46,8 +38,8 @@ public class MainActivity extends BaseActivity { // Instantiate a ViewPager and a PagerAdapter. - mPager = (ViewPager) findViewById(R.id.pager); - mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager(), this); + mPager = findViewById(R.id.pager); + mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), this); /* Toolbar and slider should have the same elevation */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -72,8 +64,8 @@ public class MainActivity extends BaseActivity { mPagerAdapter.addTab(R.string.about, AboutFragment.class); mPager.setAdapter(mPagerAdapter); - mTabs = (TabBarView) findViewById(R.id.sliding_tabs); - mTabs.setViewPager(mPager); + //mTabs = findViewById(R.id.sliding_tabs); + //mTabs.setViewPager(mPager); } private static final String FEATURE_TELEVISION = "android.hardware.type.television"; @@ -84,9 +76,9 @@ public class MainActivity extends BaseActivity { || getPackageManager().hasSystemFeature(FEATURE_LEANBACK)); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void disableToolbarElevation() { - ActionBar toolbar = getActionBar(); + ActionBar toolbar = getSupportActionBar(); toolbar.setElevation(0); } diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java index 06f1f7b7..2b6c94ad 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java +++ b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java @@ -6,19 +6,18 @@ package de.blinkt.openvpn.activities; import android.annotation.TargetApi; -import android.app.ActionBar; -import android.app.Activity; import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceActivity; -import android.support.v4n.view.ViewPager; import android.view.Menu; import android.view.MenuItem; - import android.widget.Toast; + +import androidx.appcompat.app.ActionBar; +import androidx.viewpager.widget.ViewPager; + import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ProfileManager; @@ -33,13 +32,12 @@ import de.blinkt.openvpn.fragments.Settings_UserEditable; import de.blinkt.openvpn.fragments.ShowConfigFragment; import de.blinkt.openvpn.fragments.VPNProfileList; import de.blinkt.openvpn.views.ScreenSlidePagerAdapter; -import de.blinkt.openvpn.views.TabBarView; public class VPNPreferences extends BaseActivity { - static final Class validFragments[] = new Class[] { - Settings_Authentication.class, Settings_Basic.class, Settings_IP.class, + static final Class[] validFragments = new Class[]{ + Settings_Authentication.class, Settings_Basic.class, Settings_IP.class, Settings_Obscure.class, Settings_Routing.class, ShowConfigFragment.class, Settings_Connections.class, Settings_Allowed_Apps.class }; @@ -86,7 +84,7 @@ public class VPNPreferences extends BaseActivity { } if (mProfile.mTemporaryProfile) { - Toast.makeText(this, "Temporary profiles cannot be edited", Toast.LENGTH_LONG); + Toast.makeText(this, "Temporary profiles cannot be edited", Toast.LENGTH_LONG).show(); finish(); } } @@ -127,14 +125,11 @@ public class VPNPreferences extends BaseActivity { setContentView(R.layout.main_activity); - /* Toolbar and slider should have the same elevation */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - disableToolbarElevation(); - } + disableToolbarElevation(); // Instantiate a ViewPager and a PagerAdapter. - mPager = (ViewPager) findViewById(R.id.pager); - mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager(), this); + mPager = findViewById(R.id.pager); + mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), this); Bundle fragmentArguments = new Bundle(); @@ -160,28 +155,12 @@ public class VPNPreferences extends BaseActivity { mPager.setAdapter(mPagerAdapter); - TabBarView tabs = (TabBarView) findViewById(R.id.sliding_tabs); - tabs.setViewPager(mPager); + //TabBarView tabs = (TabBarView) findViewById(R.id.sliding_tabs); + //tabs.setViewPager(mPager); } -/* - @Override - public void onBuildHeaders(List

target) { - loadHeadersFromResource(R.xml.vpn_headers, target); - Header headerToRemove=null; - for (Header header : target) { - if(header.fragmentArguments==null) - header.fragmentArguments = new Bundle(); - header.fragmentArguments.putString(getPackageName() + ".profileUUID",mProfileUUID); - if (header.id == R.id.allowed_apps_header && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) - headerToRemove = header; - } - if (headerToRemove != null) - target.remove(headerToRemove); - }*/ - @Override public void onBackPressed() { setResult(RESULT_OK, getIntent()); @@ -217,13 +196,7 @@ public class VPNPreferences extends BaseActivity { dialog.setMessage(getString(R.string.remove_vpn_query, mProfile.mName)); dialog.setPositiveButton(android.R.string.yes, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - removeProfile(mProfile); - } - - }); + (dialog1, which) -> removeProfile(mProfile)); dialog.setNegativeButton(android.R.string.no,null); dialog.create().show(); } @@ -235,9 +208,8 @@ public class VPNPreferences extends BaseActivity { } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void disableToolbarElevation() { - ActionBar toolbar = getActionBar(); + ActionBar toolbar = getSupportActionBar(); toolbar.setElevation(0); } diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java index 540f4a9a..6ab6e2cc 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java @@ -5,7 +5,6 @@ package de.blinkt.openvpn.fragments; -import android.app.Fragment; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Intent; @@ -28,6 +27,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; + import com.android.vending.billing.IInAppBillingService; import de.blinkt.openvpn.core.NativeUtils; @@ -47,8 +48,8 @@ import de.blinkt.openvpn.core.VpnStatus; public class AboutFragment extends Fragment implements View.OnClickListener { - public static final String INAPPITEM_TYPE_INAPP = "inapp"; - public static final String RESPONSE_CODE = "RESPONSE_CODE"; + private static final String INAPPITEM_TYPE_INAPP = "inapp"; + private static final String RESPONSE_CODE = "RESPONSE_CODE"; private static final int DONATION_CODE = 12; private static final int BILLING_RESPONSE_RESULT_OK = 0; private static final String RESPONSE_BUY_INTENT = "BUY_INTENT"; diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java index f9d2d47c..f40c6825 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java @@ -5,10 +5,11 @@ package de.blinkt.openvpn.fragments; -import android.app.Fragment; import android.content.Context; import android.os.Build; import android.os.Bundle; + +import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; import android.util.DisplayMetrics; @@ -184,7 +185,7 @@ public class FaqFragment extends Fragment { columns = Math.max(1, columns); - mRecyclerView = (RecyclerView) v.findViewById(R.id.faq_recycler_view); + mRecyclerView = v.findViewById(R.id.faq_recycler_view); // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/GeneralSettings.java b/main/src/ui/java/de/blinkt/openvpn/fragments/GeneralSettings.java index 34d37823..1d8b5f77 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/GeneralSettings.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/GeneralSettings.java @@ -4,8 +4,8 @@ */ package de.blinkt.openvpn.fragments; + import java.io.File; -import java.util.Collection; import android.app.AlertDialog; import android.app.AlertDialog.Builder; @@ -18,13 +18,13 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; + + +import androidx.preference.CheckBoxPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceFragmentCompat; import de.blinkt.openvpn.BuildConfig; import de.blinkt.openvpn.R; @@ -34,28 +34,28 @@ import de.blinkt.openvpn.api.ExternalAppDatabase; import de.blinkt.openvpn.core.ProfileManager; -public class GeneralSettings extends PreferenceFragment implements OnPreferenceClickListener, OnClickListener, Preference.OnPreferenceChangeListener { +public class GeneralSettings extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener, OnClickListener, Preference.OnPreferenceChangeListener { - private ExternalAppDatabase mExtapp; - private ListPreference mAlwaysOnVPN; + private ExternalAppDatabase mExtapp; + private ListPreference mAlwaysOnVPN; - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.general_settings); + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.general_settings); - PreferenceCategory devHacks = (PreferenceCategory) findPreference("device_hacks"); - mAlwaysOnVPN = (ListPreference) findPreference("alwaysOnVpn"); + + PreferenceCategory devHacks = findPreference("device_hacks"); + mAlwaysOnVPN = findPreference("alwaysOnVpn"); mAlwaysOnVPN.setOnPreferenceChangeListener(this); Preference loadtun = findPreference("loadTunModule"); - if(!isTunModuleAvailable()) { - loadtun.setEnabled(false); + if (!isTunModuleAvailable()) { + loadtun.setEnabled(false); devHacks.removePreference(loadtun); } @@ -65,59 +65,56 @@ public class GeneralSettings extends PreferenceFragment implements OnPreferenceC } CheckBoxPreference useInternalFS = (CheckBoxPreference) findPreference("useInternalFileSelector"); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) - { - devHacks.removePreference(useInternalFS); - } + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + devHacks.removePreference(useInternalFS); + } - /* Android P does not allow access to the file storage anymore */ - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) - { - Preference useInternalFileSelector = findPreference("useInternalFileSelector"); - devHacks.removePreference(useInternalFileSelector); - } + /* Android P does not allow access to the file storage anymore */ + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + Preference useInternalFileSelector = findPreference("useInternalFileSelector"); + devHacks.removePreference(useInternalFileSelector); + } - mExtapp = new ExternalAppDatabase(getActivity()); - Preference clearapi = findPreference("clearapi"); - clearapi.setOnPreferenceClickListener(this); + mExtapp = new ExternalAppDatabase(getActivity()); + Preference clearapi = findPreference("clearapi"); + clearapi.setOnPreferenceClickListener(this); - findPreference("osslspeed").setOnPreferenceClickListener(this); + findPreference("osslspeed").setOnPreferenceClickListener(this); - if(devHacks.getPreferenceCount()==0) + if (devHacks.getPreferenceCount() == 0) getPreferenceScreen().removePreference(devHacks); if (!BuildConfig.openvpn3) { PreferenceCategory appBehaviour = (PreferenceCategory) findPreference("app_behaviour"); - CheckBoxPreference ovpn3 = (CheckBoxPreference) findPreference("ovpn3"); - ovpn3.setEnabled(false); - ovpn3.setChecked(false); + CheckBoxPreference ovpn3 = (CheckBoxPreference) findPreference("ovpn3"); + ovpn3.setEnabled(false); + ovpn3.setChecked(false); } - setClearApiSummary(); - } - - @Override - public void onResume() { - super.onResume(); + setClearApiSummary(); + } + @Override + public void onResume() { + super.onResume(); VpnProfile vpn = ProfileManager.getAlwaysOnVPN(getActivity()); - StringBuffer sb = new StringBuffer(getString(R.string.defaultvpnsummary)); - sb.append('\n'); - if (vpn== null) + StringBuffer sb = new StringBuffer(getString(R.string.defaultvpnsummary)); + sb.append('\n'); + if (vpn == null) sb.append(getString(R.string.novpn_selected)); else - sb.append(getString(R.string.vpnselected, vpn.getName())); - mAlwaysOnVPN.setSummary(sb.toString()); + sb.append(getString(R.string.vpnselected, vpn.getName())); + mAlwaysOnVPN.setSummary(sb.toString()); } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - if (preference== mAlwaysOnVPN) { + if (preference == mAlwaysOnVPN) { VpnProfile vpn = ProfileManager.get(getActivity(), (String) newValue); mAlwaysOnVPN.setSummary(vpn.getName()); } @@ -125,66 +122,65 @@ public class GeneralSettings extends PreferenceFragment implements OnPreferenceC } private void setClearApiSummary() { - Preference clearapi = findPreference("clearapi"); - - if(mExtapp.getExtAppList().isEmpty()) { - clearapi.setEnabled(false); - clearapi.setSummary(R.string.no_external_app_allowed); - } else { - clearapi.setEnabled(true); - clearapi.setSummary(getString(R.string.allowed_apps, getExtAppList(", "))); - } - } - - private String getExtAppList(String delim) { - ApplicationInfo app; - PackageManager pm = getActivity().getPackageManager(); - - StringBuilder applist = new StringBuilder(); - for (String packagename : mExtapp.getExtAppList()) { - try { - app = pm.getApplicationInfo(packagename, 0); - if (applist.length() != 0) - applist.append(delim); - applist.append(app.loadLabel(pm)); - - } catch (NameNotFoundException e) { - // App not found. Remove it from the list - mExtapp.removeApp(packagename); - } - } - - return applist.toString(); - } - - private boolean isTunModuleAvailable() { - // Check if the tun module exists on the file system + Preference clearapi = findPreference("clearapi"); + + if (mExtapp.getExtAppList().isEmpty()) { + clearapi.setEnabled(false); + clearapi.setSummary(R.string.no_external_app_allowed); + } else { + clearapi.setEnabled(true); + clearapi.setSummary(getString(R.string.allowed_apps, getExtAppList(", "))); + } + } + + private String getExtAppList(String delim) { + ApplicationInfo app; + PackageManager pm = getActivity().getPackageManager(); + + StringBuilder applist = new StringBuilder(); + for (String packagename : mExtapp.getExtAppList()) { + try { + app = pm.getApplicationInfo(packagename, 0); + if (applist.length() != 0) + applist.append(delim); + applist.append(app.loadLabel(pm)); + + } catch (NameNotFoundException e) { + // App not found. Remove it from the list + mExtapp.removeApp(packagename); + } + } + + return applist.toString(); + } + + private boolean isTunModuleAvailable() { + // Check if the tun module exists on the file system return new File("/system/lib/modules/tun.ko").length() > 10; } - @Override - public boolean onPreferenceClick(Preference preference) { - if(preference.getKey().equals("clearapi")){ - Builder builder = new AlertDialog.Builder(getActivity()); - builder.setPositiveButton(R.string.clear, this); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setMessage(getString(R.string.clearappsdialog,getExtAppList("\n"))); - builder.show(); - } else if (preference.getKey().equals("osslspeed")) { - startActivity(new Intent(getActivity(), OpenSSLSpeed.class)); - } - - return true; - } - - @Override - public void onClick(DialogInterface dialog, int which) { - if( which == Dialog.BUTTON_POSITIVE){ - mExtapp.clearAllApiApps(); - setClearApiSummary(); - } - } + @Override + public boolean onPreferenceClick(Preference preference) { + if (preference.getKey().equals("clearapi")) { + Builder builder = new AlertDialog.Builder(getActivity()); + builder.setPositiveButton(R.string.clear, this); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setMessage(getString(R.string.clearappsdialog, getExtAppList("\n"))); + builder.show(); + } else if (preference.getKey().equals("osslspeed")) { + startActivity(new Intent(getActivity(), OpenSSLSpeed.class)); + } + + return true; + } + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == Dialog.BUTTON_POSITIVE) { + mExtapp.clearAllApiApps(); + setClearApiSummary(); + } + } } \ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/GraphFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/GraphFragment.java index 4ddf90c4..5d83b8fc 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/GraphFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/GraphFragment.java @@ -5,7 +5,6 @@ package de.blinkt.openvpn.fragments; -import android.app.Fragment; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -22,6 +21,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.XAxis; @@ -102,13 +102,10 @@ public class GraphFragment extends Fragment implements VpnStatus.ByteCountListen break; } - logScaleView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mLogScale = isChecked; - mChartAdapter.notifyDataSetChanged(); - getActivity().getPreferences(MODE_PRIVATE).edit().putBoolean(PREF_USE_LOG, isChecked).apply(); - } + logScaleView.setOnCheckedChangeListener((buttonView, isChecked) -> { + mLogScale = isChecked; + mChartAdapter.notifyDataSetChanged(); + getActivity().getPreferences(MODE_PRIVATE).edit().putBoolean(PREF_USE_LOG, isChecked).apply(); }); mHandler = new Handler(); diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.kt index 5008fe00..ef6eae69 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.kt +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/KeyChainSettingsFragment.kt @@ -46,7 +46,7 @@ internal abstract class KeyChainSettingsFragment : Settings_Fragment(), View.OnC @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @Throws(KeyChainException::class, InterruptedException::class) get() { - val key: PrivateKey = KeyChain.getPrivateKey(activity.applicationContext, mProfile.mAlias) ?: return false + val key: PrivateKey = KeyChain.getPrivateKey(activity!!.applicationContext, mProfile.mAlias) ?: return false if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { val keyFactory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore") @@ -86,9 +86,9 @@ internal abstract class KeyChainSettingsFragment : Settings_Fragment(), View.OnC object : Thread() { override fun run() { try { - val b = ExtAuthHelper.getCertificateMetaData(activity, mProfile.mExternalAuthenticator, mProfile.mAlias) + val b = ExtAuthHelper.getCertificateMetaData(context!!, mProfile.mExternalAuthenticator, mProfile.mAlias) mProfile.mAlias = b.getString(ExtAuthHelper.EXTRA_ALIAS) - activity.runOnUiThread { setAlias() } + activity!!.runOnUiThread { setAlias() } } catch (e: KeyChainException) { e.printStackTrace() } @@ -108,14 +108,14 @@ internal abstract class KeyChainSettingsFragment : Settings_Fragment(), View.OnC if (external) { if (!TextUtils.isEmpty(mProfile.mExternalAuthenticator) && !TextUtils.isEmpty(mProfile.mAlias)) { - cert = ExtAuthHelper.getCertificateChain(activity, mProfile.mExternalAuthenticator, mProfile.mAlias)!![0] - metadata = ExtAuthHelper.getCertificateMetaData(activity, mProfile.mExternalAuthenticator, mProfile.mAlias) + cert = ExtAuthHelper.getCertificateChain(context!!, mProfile.mExternalAuthenticator, mProfile.mAlias)!![0] + metadata = ExtAuthHelper.getCertificateMetaData(context!!, mProfile.mExternalAuthenticator, mProfile.mAlias) } else { cert = null certstr = getString(R.string.extauth_not_configured) } } else { - val certChain = KeyChain.getCertificateChain(activity.applicationContext, mProfile.mAlias) + val certChain = KeyChain.getCertificateChain(activity!!.applicationContext, mProfile.mAlias) if (certChain != null) { cert = certChain[0] if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { @@ -137,7 +137,7 @@ internal abstract class KeyChainSettingsFragment : Settings_Fragment(), View.OnC val certStringCopy = certstr val finalMetadata = metadata - activity.runOnUiThread { + activity!!.runOnUiThread { mAliasCertificate.text = certStringCopy if (finalMetadata != null) mExtAliasName.text = finalMetadata.getString(ExtAuthHelper.EXTRA_DESCRIPTION) @@ -210,7 +210,7 @@ internal abstract class KeyChainSettingsFragment : Settings_Fragment(), View.OnC fun showCertDialog() { try { - KeyChain.choosePrivateKeyAlias(activity, + KeyChain.choosePrivateKeyAlias(activity!!, { alias -> // Credential alias selected. Remember the alias selection for future use. mProfile.mAlias = alias @@ -248,10 +248,10 @@ internal abstract class KeyChainSettingsFragment : Settings_Fragment(), View.OnC return true } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - if (requestCode == UPDATEE_EXT_ALIAS && resultCode == Activity.RESULT_OK) { + if (data != null && requestCode == UPDATEE_EXT_ALIAS && resultCode == Activity.RESULT_OK) { mProfile.mAlias = data.getStringExtra(ExtAuthHelper.EXTRA_ALIAS) mExtAliasName.text = data.getStringExtra(ExtAuthHelper.EXTRA_DESCRIPTION) } diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java index 32da0ac5..be2007e5 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java @@ -9,8 +9,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.app.Activity; -import android.app.AlertDialog; -import android.app.ListFragment; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -25,6 +23,9 @@ import android.os.Handler.Callback; import android.os.Message; import android.preference.PreferenceManager; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.ListFragment; + import android.text.SpannableString; import android.text.format.DateFormat; import android.text.style.ImageSpan; @@ -529,16 +530,11 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. dialog.setPositiveButton(R.string.restart, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Intent intent = new Intent(getActivity(), LaunchVPN.class); - intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUIDString()); - intent.setAction(Intent.ACTION_MAIN); - startActivity(intent); - } - - + (dialog1, which) -> { + Intent intent = new Intent(getActivity(), LaunchVPN.class); + intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUIDString()); + intent.setAction(Intent.ACTION_MAIN); + startActivity(intent); }); dialog.setNegativeButton(R.string.ignore, null); dialog.create().show(); @@ -564,18 +560,13 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. super.onActivityCreated(savedInstanceState); ListView lv = getListView(); - lv.setOnItemLongClickListener(new OnItemLongClickListener() { - - @Override - public boolean onItemLongClick(AdapterView parent, View view, - int position, long id) { - ClipboardManager clipboard = (ClipboardManager) - getActivity().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Log Entry", ((TextView) view).getText()); - clipboard.setPrimaryClip(clip); - Toast.makeText(getActivity(), R.string.copied_entry, Toast.LENGTH_SHORT).show(); - return true; - } + lv.setOnItemLongClickListener((parent, view, position, id) -> { + ClipboardManager clipboard = (ClipboardManager) + getActivity().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Log Entry", ((TextView) view).getText()); + clipboard.setPrimaryClip(clip); + Toast.makeText(getActivity(), R.string.copied_entry, Toast.LENGTH_SHORT).show(); + return true; }); } diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java index 9ac8bebb..212c5e71 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java @@ -6,12 +6,14 @@ package de.blinkt.openvpn.fragments; import android.os.Bundle; -import android.preference.PreferenceFragment; + +import androidx.preference.PreferenceFragmentCompat; + import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ProfileManager; -public abstract class OpenVpnPreferencesFragment extends PreferenceFragment { +public abstract class OpenVpnPreferencesFragment extends PreferenceFragmentCompat { protected VpnProfile mProfile; diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/SendDumpFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/SendDumpFragment.java index 0fe40905..5bc0bbb1 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/SendDumpFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/SendDumpFragment.java @@ -9,7 +9,6 @@ import java.io.File; import java.util.ArrayList; import java.util.Date; -import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; @@ -23,106 +22,95 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; + import de.blinkt.openvpn.R; import de.blinkt.openvpn.core.VpnStatus; -public class SendDumpFragment extends Fragment { - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - final View v = inflater.inflate(R.layout.fragment_senddump, container, false); - v.findViewById(R.id.senddump).setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - emailMiniDumps(); - } - }); - - new Thread(new Runnable() { - @Override - public void run() { - final Pair ldump = getLastestDump(getActivity()); - if (ldump==null) - return; - // Do in background since it does I/O - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - TextView dumpDateText = (TextView) v.findViewById(R.id.dumpdate); - String datestr = (new Date(ldump.second)).toString(); - long timediff = System.currentTimeMillis() - ldump.second; - long minutes = timediff / 1000 / 60 % 60; - long hours = timediff / 1000 / 60 / 60; - dumpDateText.setText(getString(R.string.lastdumpdate, hours, minutes, datestr)); - - } - }); - } - }).start(); - return v; - } - - public void emailMiniDumps() - { - //need to "send multiple" to get more than one attachment - final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE); - emailIntent.setType("*/*"); - emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, - new String[]{"Arne Schwabe "}); - - String version; - String name="ics-openvpn"; - try { - PackageInfo packageinfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0); - version = packageinfo.versionName; - name = packageinfo.applicationInfo.name; - } catch (NameNotFoundException e) { - version = "error fetching version"; - } - - - emailIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("%s(%s) %s Minidump",name, getActivity().getPackageName(), version)); - - emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe the issue you have experienced"); - - ArrayList uris = new ArrayList<>(); - - Pair ldump = getLastestDump(getActivity()); - if(ldump==null) { - VpnStatus.logError("No Minidump found!"); - } - - uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.first.getName())); - uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.first.getName() + ".log")); - - emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); - startActivity(emailIntent); - } - - static public Pair getLastestDump(Context c) { - long newestDumpTime=0; - File newestDumpFile=null; - - if (c.getCacheDir() ==null) +public class SendDumpFragment extends Fragment { + + static public Pair getLastestDump(Context c) { + long newestDumpTime = 0; + File newestDumpFile = null; + + if (c.getCacheDir() == null) + return null; + + for (File f : c.getCacheDir().listFiles()) { + if (!f.getName().endsWith(".dmp")) + continue; + + if (newestDumpTime < f.lastModified()) { + newestDumpTime = f.lastModified(); + newestDumpFile = f; + } + } + // Ignore old dumps + if (System.currentTimeMillis() - 48 * 60 * 1000 > newestDumpTime) return null; - for(File f:c.getCacheDir().listFiles()) { - if(!f.getName().endsWith(".dmp")) - continue; - - if (newestDumpTime < f.lastModified()) { - newestDumpTime = f.lastModified(); - newestDumpFile=f; - } - } - // Ignore old dumps - if(System.currentTimeMillis() - 48 * 60 * 1000 > newestDumpTime ) - return null; - - return Pair.create(newestDumpFile, newestDumpTime); - } + return Pair.create(newestDumpFile, newestDumpTime); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + final View v = inflater.inflate(R.layout.fragment_senddump, container, false); + v.findViewById(R.id.senddump).setOnClickListener(v1 -> emailMiniDumps()); + + new Thread(() -> { + final Pair ldump = getLastestDump(getActivity()); + if (ldump == null) + return; + // Do in background since it does I/O + getActivity().runOnUiThread(() -> { + TextView dumpDateText = (TextView) v.findViewById(R.id.dumpdate); + String datestr = (new Date(ldump.second)).toString(); + long timediff = System.currentTimeMillis() - ldump.second; + long minutes = timediff / 1000 / 60 % 60; + long hours = timediff / 1000 / 60 / 60; + dumpDateText.setText(getString(R.string.lastdumpdate, hours, minutes, datestr)); + + }); + }).start(); + return v; + } + + public void emailMiniDumps() { + //need to "send multiple" to get more than one attachment + final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE); + emailIntent.setType("*/*"); + emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, + new String[]{"Arne Schwabe "}); + + String version; + String name = "ics-openvpn"; + try { + PackageInfo packageinfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0); + version = packageinfo.versionName; + name = packageinfo.applicationInfo.name; + } catch (NameNotFoundException e) { + version = "error fetching version"; + } + + + emailIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("%s(%s) %s Minidump", name, getActivity().getPackageName(), version)); + + emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe the issue you have experienced"); + + ArrayList uris = new ArrayList<>(); + + Pair ldump = getLastestDump(getActivity()); + if (ldump == null) { + VpnStatus.logError("No Minidump found!"); + } + + uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.first.getName())); + uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.first.getName() + ".log")); + + emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); + startActivity(emailIntent); + } } diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Allowed_Apps.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Allowed_Apps.kt index dd2aa3b7..e3879775 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Allowed_Apps.kt +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Allowed_Apps.kt @@ -6,8 +6,6 @@ package de.blinkt.openvpn.fragments import android.Manifest -import android.app.Activity -import android.app.Fragment import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager @@ -29,6 +27,7 @@ import android.widget.ListView import android.widget.SearchView import android.widget.Switch import android.widget.TextView +import androidx.fragment.app.Fragment import java.util.Collections import java.util.Locale @@ -37,6 +36,7 @@ import java.util.Vector import de.blinkt.openvpn.R import de.blinkt.openvpn.VpnProfile import de.blinkt.openvpn.core.ProfileManager +import org.jetbrains.anko.runOnUiThread /** * Created by arne on 16.11.14. @@ -75,9 +75,9 @@ class Settings_Allowed_Apps : Fragment(), AdapterView.OnItemClickListener, Compo override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val profileUuid = arguments.getString(activity.packageName + ".profileUUID") + val profileUuid = requireArguments().getString(activity!!.packageName + ".profileUUID") mProfile = ProfileManager.get(activity, profileUuid) - activity.title = getString(R.string.edit_profile_title, mProfile.name) + activity!!.title = getString(R.string.edit_profile_title, mProfile.name) setHasOptionsMenu(true) } @@ -116,7 +116,7 @@ class Settings_Allowed_Apps : Fragment(), AdapterView.OnItemClickListener, Compo val vpnOnDefaultSwitch = mSettingsView.findViewById(R.id.default_allow) as Switch - vpnOnDefaultSwitch.setOnCheckedChangeListener { buttonView, isChecked -> + vpnOnDefaultSwitch.setOnCheckedChangeListener { _, isChecked -> changeDisallowText(isChecked) mProfile.mAllowedAppsVpnAreDisallowed = isChecked } @@ -125,19 +125,19 @@ class Settings_Allowed_Apps : Fragment(), AdapterView.OnItemClickListener, Compo val vpnAllowBypassSwitch = mSettingsView.findViewById(R.id.allow_bypass) as Switch - vpnAllowBypassSwitch.setOnCheckedChangeListener { buttonView, isChecked -> mProfile.mAllowAppVpnBypass = isChecked } + vpnAllowBypassSwitch.setOnCheckedChangeListener { _, isChecked -> mProfile.mAllowAppVpnBypass = isChecked } vpnAllowBypassSwitch.isChecked = mProfile.mAllowAppVpnBypass mListView = v.findViewById(android.R.id.list) as ListView - mListAdapter = PackageAdapter(activity, mProfile) + mListAdapter = PackageAdapter(requireContext(), mProfile) mListView.adapter = mListAdapter mListView.onItemClickListener = this mListView.emptyView = v.findViewById(R.id.loading_container) - Thread(Runnable { mListAdapter.populateList(activity) }).start() + Thread(Runnable { mListAdapter.populateList(requireContext()) }).start() return v } @@ -197,7 +197,7 @@ class Settings_Allowed_Apps : Fragment(), AdapterView.OnItemClickListener, Compo private val mProfile = vp - fun populateList(c: Activity) { + fun populateList(c: Context) { val installedPackages = mPm.getInstalledApplications(PackageManager.GET_META_DATA) // Remove apps not using Internet @@ -282,11 +282,11 @@ class Settings_Allowed_Apps : Fragment(), AdapterView.OnItemClickListener, Compo } private inner class ItemFilter : Filter() { - override fun performFiltering(constraint: CharSequence): Filter.FilterResults { + override fun performFiltering(constraint: CharSequence): FilterResults { val filterString = constraint.toString().toLowerCase(Locale.getDefault()) - val results = Filter.FilterResults() + val results = FilterResults() val count = mPackages.size @@ -313,7 +313,7 @@ class Settings_Allowed_Apps : Fragment(), AdapterView.OnItemClickListener, Compo return results } - override fun publishResults(constraint: CharSequence, results: Filter.FilterResults) { + override fun publishResults(constraint: CharSequence, results: FilterResults) { mFilteredData = results.values as Vector notifyDataSetChanged() } diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.java b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.java index 8fd6aa98..b58db81a 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.java @@ -9,91 +9,98 @@ import android.app.Activity; import android.content.Intent; import android.os.Build; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.SwitchPreference; import android.text.TextUtils; import android.util.Pair; + +import androidx.fragment.app.DialogFragment; +import androidx.preference.CheckBoxPreference; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.SwitchPreference; + import de.blinkt.openvpn.activities.FileSelect; import de.blinkt.openvpn.R; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.views.RemoteCNPreference; import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.views.RemoteCNPreferenceDialog; import java.io.IOException; -public class Settings_Authentication extends OpenVpnPreferencesFragment implements OnPreferenceChangeListener, OnPreferenceClickListener { - private static final int SELECT_TLS_FILE_LEGACY_DIALOG = 23223232; - private static final int SELECT_TLS_FILE_KITKAT = SELECT_TLS_FILE_LEGACY_DIALOG +1; +public class Settings_Authentication extends OpenVpnPreferencesFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { + private static final int SELECT_TLS_FILE_LEGACY_DIALOG = 23223232; + private static final int SELECT_TLS_FILE_KITKAT = SELECT_TLS_FILE_LEGACY_DIALOG + 1; private CheckBoxPreference mExpectTLSCert; - private CheckBoxPreference mCheckRemoteCN; - private RemoteCNPreference mRemoteCN; - private ListPreference mTLSAuthDirection; - private Preference mTLSAuthFile; - private SwitchPreference mUseTLSAuth; - private EditTextPreference mCipher; - private String mTlsAuthFileData; - private EditTextPreference mAuth; + private CheckBoxPreference mCheckRemoteCN; + private RemoteCNPreference mRemoteCN; + private ListPreference mTLSAuthDirection; + private Preference mTLSAuthFile; + private SwitchPreference mUseTLSAuth; + private EditTextPreference mCipher; + private String mTlsAuthFileData; + private EditTextPreference mAuth; private EditTextPreference mRemoteX509Name; @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.vpn_authentification); + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.vpn_authentification); - mExpectTLSCert = (CheckBoxPreference) findPreference("remoteServerTLS"); - mCheckRemoteCN = (CheckBoxPreference) findPreference("checkRemoteCN"); - mRemoteCN = (RemoteCNPreference) findPreference("remotecn"); - mRemoteCN.setOnPreferenceChangeListener(this); + mExpectTLSCert = findPreference("remoteServerTLS"); + mCheckRemoteCN = (CheckBoxPreference) findPreference("checkRemoteCN"); + mRemoteCN = (RemoteCNPreference) findPreference("remotecn"); + mRemoteCN.setOnPreferenceChangeListener(this); - mRemoteX509Name = (EditTextPreference) findPreference("remotex509name"); + mRemoteX509Name = (EditTextPreference) findPreference("remotex509name"); mRemoteX509Name.setOnPreferenceChangeListener(this); - mUseTLSAuth = (SwitchPreference) findPreference("useTLSAuth" ); - mTLSAuthFile = findPreference("tlsAuthFile"); - mTLSAuthDirection = (ListPreference) findPreference("tls_direction"); + mUseTLSAuth = (SwitchPreference) findPreference("useTLSAuth"); + mTLSAuthFile = findPreference("tlsAuthFile"); + mTLSAuthDirection = (ListPreference) findPreference("tls_direction"); + + + mTLSAuthFile.setOnPreferenceClickListener(this); + mCipher = (EditTextPreference) findPreference("cipher"); + mCipher.setOnPreferenceChangeListener(this); - mTLSAuthFile.setOnPreferenceClickListener(this); + mAuth = (EditTextPreference) findPreference("auth"); + mAuth.setOnPreferenceChangeListener(this); - mCipher =(EditTextPreference) findPreference("cipher"); - mCipher.setOnPreferenceChangeListener(this); + loadSettings(); - mAuth =(EditTextPreference) findPreference("auth"); - mAuth.setOnPreferenceChangeListener(this); + } - loadSettings(); + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - } + } - @Override - protected void loadSettings() { + @Override + protected void loadSettings() { - mExpectTLSCert.setChecked(mProfile.mExpectTLSCert); - mCheckRemoteCN.setChecked(mProfile.mCheckRemoteCN); - mRemoteCN.setDN(mProfile.mRemoteCN); - mRemoteCN.setAuthType(mProfile.mX509AuthType); - onPreferenceChange(mRemoteCN, - new Pair(mProfile.mX509AuthType, mProfile.mRemoteCN)); + mExpectTLSCert.setChecked(mProfile.mExpectTLSCert); + mCheckRemoteCN.setChecked(mProfile.mCheckRemoteCN); + mRemoteCN.setDN(mProfile.mRemoteCN); + mRemoteCN.setAuthType(mProfile.mX509AuthType); + onPreferenceChange(mRemoteCN, + new Pair(mProfile.mX509AuthType, mProfile.mRemoteCN)); mRemoteX509Name.setText(mProfile.mx509UsernameField); onPreferenceChange(mRemoteX509Name, mProfile.mx509UsernameField); - mUseTLSAuth.setChecked(mProfile.mUseTLSAuth); - mTlsAuthFileData= mProfile.mTLSAuthFilename; - setTlsAuthSummary(mTlsAuthFileData); - mTLSAuthDirection.setValue(mProfile.mTLSAuthDirection); - mCipher.setText(mProfile.mCipher); - onPreferenceChange(mCipher, mProfile.mCipher); - mAuth.setText(mProfile.mAuth); - onPreferenceChange(mAuth, mProfile.mAuth); + mUseTLSAuth.setChecked(mProfile.mUseTLSAuth); + mTlsAuthFileData = mProfile.mTLSAuthFilename; + setTlsAuthSummary(mTlsAuthFileData); + mTLSAuthDirection.setValue(mProfile.mTLSAuthDirection); + mCipher.setText(mProfile.mCipher); + onPreferenceChange(mCipher, mProfile.mCipher); + mAuth.setText(mProfile.mAuth); + onPreferenceChange(mAuth, mProfile.mAuth); if (mProfile.mAuthenticationType == VpnProfile.TYPE_STATICKEYS) { mExpectTLSCert.setEnabled(false); @@ -104,47 +111,46 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen mCheckRemoteCN.setEnabled(true); } - } - - @Override - protected void saveSettings() { - mProfile.mExpectTLSCert=mExpectTLSCert.isChecked(); - mProfile.mCheckRemoteCN=mCheckRemoteCN.isChecked(); - mProfile.mRemoteCN=mRemoteCN.getCNText(); - mProfile.mX509AuthType=mRemoteCN.getAuthtype(); + } - mProfile.mUseTLSAuth = mUseTLSAuth.isChecked(); - mProfile.mTLSAuthFilename = mTlsAuthFileData; + @Override + protected void saveSettings() { + mProfile.mExpectTLSCert = mExpectTLSCert.isChecked(); + mProfile.mCheckRemoteCN = mCheckRemoteCN.isChecked(); + mProfile.mRemoteCN = mRemoteCN.getCNText(); + mProfile.mX509AuthType = mRemoteCN.getAuthtype(); + + mProfile.mUseTLSAuth = mUseTLSAuth.isChecked(); + mProfile.mTLSAuthFilename = mTlsAuthFileData; mProfile.mx509UsernameField = mRemoteX509Name.getText(); - if(mTLSAuthDirection.getValue()==null) - mProfile.mTLSAuthDirection=null; - else - mProfile.mTLSAuthDirection = mTLSAuthDirection.getValue(); - - if(mCipher.getText()==null) - mProfile.mCipher=null; - else - mProfile.mCipher = mCipher.getText(); + if (mTLSAuthDirection.getValue() == null) + mProfile.mTLSAuthDirection = null; + else + mProfile.mTLSAuthDirection = mTLSAuthDirection.getValue(); - if(mAuth.getText()==null) - mProfile.mAuth = null; - else - mProfile.mAuth = mAuth.getText(); - - } + if (mCipher.getText() == null) + mProfile.mCipher = null; + else + mProfile.mCipher = mCipher.getText(); + if (mAuth.getText() == null) + mProfile.mAuth = null; + else + mProfile.mAuth = mAuth.getText(); + } - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if(preference==mRemoteCN) { - @SuppressWarnings("unchecked") - int authtype = ((Pair) newValue).first; - @SuppressWarnings("unchecked") - String dn = ((Pair) newValue).second; - if ("".equals(dn)) { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mRemoteCN) { + @SuppressWarnings("unchecked") + int authtype = ((Pair) newValue).first; + @SuppressWarnings("unchecked") + String dn = ((Pair) newValue).second; + + if ("".equals(dn)) { if (mProfile.mConnections.length > 0) { preference.setSummary(getX509String(VpnProfile.X509_VERIFY_TLSREMOTE_RDN, mProfile.mConnections[0].mServerName)); } else { @@ -154,39 +160,40 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen preference.setSummary(getX509String(authtype, dn)); } - } else if (preference == mCipher || preference == mAuth) { - preference.setSummary((CharSequence) newValue); - } else if (preference == mRemoteX509Name) { - preference.setSummary(TextUtils.isEmpty((CharSequence) newValue) ? "CN (default)" : (CharSequence)newValue); + } else if (preference == mCipher || preference == mAuth) { + preference.setSummary((CharSequence) newValue); + } else if (preference == mRemoteX509Name) { + preference.setSummary(TextUtils.isEmpty((CharSequence) newValue) ? "CN (default)" : (CharSequence) newValue); + } + return true; + } + + private CharSequence getX509String(int authtype, String dn) { + String ret = ""; + switch (authtype) { + case VpnProfile.X509_VERIFY_TLSREMOTE: + case VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING: + ret += "tls-remote "; + break; + + case VpnProfile.X509_VERIFY_TLSREMOTE_DN: + ret = "dn: "; + break; + + case VpnProfile.X509_VERIFY_TLSREMOTE_RDN: + ret = "rdn: "; + break; + + case VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX: + ret = "rdn prefix: "; + break; } - return true; - } - private CharSequence getX509String(int authtype, String dn) { - String ret =""; - switch (authtype) { - case VpnProfile.X509_VERIFY_TLSREMOTE: - case VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING: - ret+="tls-remote "; - break; - - case VpnProfile.X509_VERIFY_TLSREMOTE_DN: - ret="dn: "; - break; - - case VpnProfile.X509_VERIFY_TLSREMOTE_RDN: - ret="rdn: "; - break; - - case VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX: - ret="rdn prefix: "; - break; - } - return ret + dn; - } + return ret + dn; + } void startFileDialog() { Intent startFC = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && ! Utils.alwaysUseOldFileChooser(getActivity())) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !Utils.alwaysUseOldFileChooser(getActivity())) { startFC = Utils.getFilePickerIntent(getActivity(), Utils.FileType.TLS_AUTH_FILE); startActivityForResult(startFC, SELECT_TLS_FILE_KITKAT); } @@ -200,22 +207,22 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen } @Override - public boolean onPreferenceClick(Preference preference) { - startFileDialog(); - return true; - - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if(requestCode == SELECT_TLS_FILE_LEGACY_DIALOG && resultCode == Activity.RESULT_OK){ - String result = data.getStringExtra(FileSelect.RESULT_DATA); - mTlsAuthFileData=result; - setTlsAuthSummary(result); - } else if (requestCode == SELECT_TLS_FILE_KITKAT && resultCode == Activity.RESULT_OK) { + public boolean onPreferenceClick(Preference preference) { + startFileDialog(); + return true; + + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == SELECT_TLS_FILE_LEGACY_DIALOG && resultCode == Activity.RESULT_OK) { + String result = data.getStringExtra(FileSelect.RESULT_DATA); + mTlsAuthFileData = result; + setTlsAuthSummary(result); + } else if (requestCode == SELECT_TLS_FILE_KITKAT && resultCode == Activity.RESULT_OK) { try { - mTlsAuthFileData= Utils.getFilePickerResult(Utils.FileType.TLS_AUTH_FILE,data,getActivity()); + mTlsAuthFileData = Utils.getFilePickerResult(Utils.FileType.TLS_AUTH_FILE, data, getActivity()); setTlsAuthSummary(mTlsAuthFileData); } catch (IOException e) { VpnStatus.logException(e); @@ -223,16 +230,31 @@ public class Settings_Authentication extends OpenVpnPreferencesFragment implemen VpnStatus.logException(se); } } - } + } - private void setTlsAuthSummary(String result) { - if(result==null) + private void setTlsAuthSummary(String result) { + if (result == null) result = getString(R.string.no_certificate); - if(result.startsWith(VpnProfile.INLINE_TAG)) - mTLSAuthFile.setSummary(R.string.inline_file_data); + if (result.startsWith(VpnProfile.INLINE_TAG)) + mTLSAuthFile.setSummary(R.string.inline_file_data); else if (result.startsWith(VpnProfile.DISPLAYNAME_TAG)) - mTLSAuthFile.setSummary(getString(R.string.imported_from_file, VpnProfile.getDisplayName(result))); - else - mTLSAuthFile.setSummary(result); - } + mTLSAuthFile.setSummary(getString(R.string.imported_from_file, VpnProfile.getDisplayName(result))); + else + mTLSAuthFile.setSummary(result); + } + + @Override + public void onDisplayPreferenceDialog(Preference preference) { + DialogFragment dialogFragment = null; + if (preference instanceof RemoteCNPreference) { + dialogFragment = RemoteCNPreferenceDialog.newInstance(preference.getKey()); + } + + if (dialogFragment != null) { + dialogFragment.setTargetFragment(this, 0); + dialogFragment.show(requireFragmentManager(), "RemoteCNDialog"); + } else { + super.onDisplayPreferenceDialog(preference); + } + } } \ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Fragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Fragment.java index 738bd0e9..f29f2063 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Fragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Fragment.java @@ -5,9 +5,10 @@ package de.blinkt.openvpn.fragments; -import android.app.Fragment; import android.os.Bundle; +import androidx.fragment.app.Fragment; + import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ProfileManager; diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.java b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.java index bd2ad5f2..843ff750 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.java @@ -4,17 +4,19 @@ */ package de.blinkt.openvpn.fragments; + import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceManager; -import android.preference.SwitchPreference; + +import androidx.preference.CheckBoxPreference; +import androidx.preference.EditTextPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceManager; +import androidx.preference.SwitchPreference; + import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; -public class Settings_IP extends OpenVpnPreferencesFragment implements OnPreferenceChangeListener { +public class Settings_IP extends OpenVpnPreferencesFragment implements Preference.OnPreferenceChangeListener { private EditTextPreference mIPv4; private EditTextPreference mIPv6; private SwitchPreference mUsePull; @@ -33,19 +35,19 @@ public class Settings_IP extends OpenVpnPreferencesFragment implements OnPrefere // Make sure default values are applied. In a real app, you would // want this in a shared function that is used to retrieve the // SharedPreferences wherever they are needed. - PreferenceManager.setDefaultValues(getActivity(), + PreferenceManager.setDefaultValues(requireActivity(), R.xml.vpn_ipsettings, false); // Load the preferences from an XML resource addPreferencesFromResource(R.xml.vpn_ipsettings); - mIPv4 = (EditTextPreference) findPreference("ipv4_address"); - mIPv6 = (EditTextPreference) findPreference("ipv6_address"); - mUsePull = (SwitchPreference) findPreference("usePull"); - mOverrideDNS = (CheckBoxPreference) findPreference("overrideDNS"); - mSearchdomain =(EditTextPreference) findPreference("searchdomain"); - mDNS1 = (EditTextPreference) findPreference("dns1"); - mDNS2 = (EditTextPreference) findPreference("dns2"); - mNobind = (CheckBoxPreference) findPreference("nobind"); + mIPv4 = findPreference("ipv4_address"); + mIPv6 = findPreference("ipv6_address"); + mUsePull = findPreference("usePull"); + mOverrideDNS = findPreference("overrideDNS"); + mSearchdomain = findPreference("searchdomain"); + mDNS1 = findPreference("dns1"); + mDNS2 = findPreference("dns2"); + mNobind = findPreference("nobind"); mIPv4.setOnPreferenceChangeListener(this); mIPv6.setOnPreferenceChangeListener(this); @@ -141,4 +143,8 @@ public class Settings_IP extends OpenVpnPreferencesFragment implements OnPrefere } - } \ No newline at end of file + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + + } +} \ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Obscure.java b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Obscure.java index 6674599d..4b1db582 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Obscure.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Obscure.java @@ -6,19 +6,20 @@ package de.blinkt.openvpn.fragments; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; import android.widget.Toast; + +import androidx.preference.CheckBoxPreference; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; + import java.util.Locale; import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; -public class Settings_Obscure extends OpenVpnPreferencesFragment implements OnPreferenceChangeListener { +public class Settings_Obscure extends OpenVpnPreferencesFragment implements Preference.OnPreferenceChangeListener { private CheckBoxPreference mUseRandomHostName; private CheckBoxPreference mUseFloat; private CheckBoxPreference mUseCustomConfig; @@ -126,6 +127,11 @@ public class Settings_Obscure extends OpenVpnPreferencesFragment implements OnPr } + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + + } + protected void loadSettings() { mUseRandomHostName.setChecked(mProfile.mUseRandomHostname); mUseFloat.setChecked(mProfile.mUseFloat); diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Routing.java b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Routing.java index 91a41ef3..6b963bb8 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Routing.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Routing.java @@ -6,14 +6,15 @@ package de.blinkt.openvpn.fragments; import android.os.Build; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; + +import androidx.preference.CheckBoxPreference; +import androidx.preference.EditTextPreference; +import androidx.preference.Preference; + import de.blinkt.openvpn.R; -public class Settings_Routing extends OpenVpnPreferencesFragment implements OnPreferenceChangeListener { +public class Settings_Routing extends OpenVpnPreferencesFragment implements Preference.OnPreferenceChangeListener { private EditTextPreference mCustomRoutes; private CheckBoxPreference mUseDefaultRoute; private EditTextPreference mCustomRoutesv6; @@ -30,17 +31,17 @@ public class Settings_Routing extends OpenVpnPreferencesFragment implements OnPr // Load the preferences from an XML resource addPreferencesFromResource(R.xml.vpn_routing); - mCustomRoutes = (EditTextPreference) findPreference("customRoutes"); - mUseDefaultRoute = (CheckBoxPreference) findPreference("useDefaultRoute"); - mCustomRoutesv6 = (EditTextPreference) findPreference("customRoutesv6"); - mUseDefaultRoutev6 = (CheckBoxPreference) findPreference("useDefaultRoutev6"); - mExcludedRoutes = (EditTextPreference) findPreference("excludedRoutes"); - mExcludedRoutesv6 = (EditTextPreference) findPreference("excludedRoutesv6"); + mCustomRoutes = findPreference("customRoutes"); + mUseDefaultRoute = findPreference("useDefaultRoute"); + mCustomRoutesv6 = findPreference("customRoutesv6"); + mUseDefaultRoutev6 = findPreference("useDefaultRoutev6"); + mExcludedRoutes = findPreference("excludedRoutes"); + mExcludedRoutesv6 = findPreference("excludedRoutesv6"); - mRouteNoPull = (CheckBoxPreference) findPreference("routenopull"); - mLocalVPNAccess = (CheckBoxPreference) findPreference("unblockLocal"); + mRouteNoPull = findPreference("routenopull"); + mLocalVPNAccess = findPreference("unblockLocal"); - mBlockUnusedAF = (CheckBoxPreference) findPreference("blockUnusedAF"); + mBlockUnusedAF = findPreference("blockUnusedAF"); mCustomRoutes.setOnPreferenceChangeListener(this); mCustomRoutesv6.setOnPreferenceChangeListener(this); @@ -54,6 +55,11 @@ public class Settings_Routing extends OpenVpnPreferencesFragment implements OnPr loadSettings(); } + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + + } + @Override protected void loadSettings() { diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/ShowConfigFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/ShowConfigFragment.java index f5c1750a..d69b4581 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/ShowConfigFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/ShowConfigFragment.java @@ -5,7 +5,6 @@ package de.blinkt.openvpn.fragments; -import android.app.Fragment; import android.content.Intent; import android.os.Build; import android.os.Bundle; @@ -17,6 +16,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ProfileManager; @@ -30,17 +33,12 @@ public class ShowConfigFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v=inflater.inflate(R.layout.viewconfig, container,false); - mConfigView = (TextView) v.findViewById(R.id.configview); + mConfigView = v.findViewById(R.id.configview); - mfabButton = (ImageButton) v.findViewById(R.id.share_config); + mfabButton = v.findViewById(R.id.share_config); if (mfabButton!=null) { - mfabButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - shareConfig(); - } - }); + mfabButton.setOnClickListener(v1 -> shareConfig()); mfabButton.setVisibility(View.INVISIBLE); } return v; @@ -53,12 +51,12 @@ public class ShowConfigFragment extends Fragment { /* Add a few newlines to make the textview scrollable past the FAB */ try { - configtext = vp.getConfigFile(getActivity(), VpnProfile.doUseOpenVPN3(getActivity())) + "\n\n\n"; + configtext = vp.getConfigFile(requireContext(), VpnProfile.doUseOpenVPN3(getActivity())) + "\n\n\n"; } catch (Exception e) { e.printStackTrace(); configtext = "Error generating config file: " + e.getLocalizedMessage(); } - getActivity().runOnUiThread(() -> { + requireActivity().runOnUiThread(() -> { cv.setText(configtext); if (mfabButton!=null) mfabButton.setVisibility(View.VISIBLE); @@ -76,7 +74,7 @@ public class ShowConfigFragment extends Fragment { } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) inflater.inflate(R.menu.configmenu, menu); } @@ -108,7 +106,7 @@ public class ShowConfigFragment extends Fragment { } private void populateConfigText() { - String profileUUID = getArguments().getString(getActivity().getPackageName() + ".profileUUID"); + String profileUUID = requireArguments().getString(requireActivity().getPackageName() + ".profileUUID"); final VpnProfile vp = ProfileManager.get(getActivity(),profileUUID); int check=vp.checkProfile(getActivity()); diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java index c1aadb7e..eaccd201 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java @@ -8,7 +8,6 @@ package de.blinkt.openvpn.fragments; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; -import android.app.ListFragment; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -21,6 +20,8 @@ import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; import androidx.annotation.RequiresApi; +import androidx.fragment.app.ListFragment; + import android.text.Html; import android.text.Html.ImageGetter; import android.view.LayoutInflater; @@ -76,12 +77,9 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn @Override public void updateState(String state, String logmessage, final int localizedResId, ConnectionStatus level) { - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - mLastStatusMessage = VpnStatus.getLastCleanLogMessage(getActivity()); - mArrayadapter.notifyDataSetChanged(); - } + getActivity().runOnUiThread(() -> { + mLastStatusMessage = VpnStatus.getLastCleanLogMessage(getActivity()); + mArrayadapter.notifyDataSetChanged(); }); } diff --git a/main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java b/main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java index e8328f5c..46722e03 100644 --- a/main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java +++ b/main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java @@ -6,8 +6,10 @@ package de.blinkt.openvpn.views; import android.content.Context; -import android.preference.ListPreference; import android.util.AttributeSet; + +import androidx.preference.ListPreference; + import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ProfileManager; diff --git a/main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java b/main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java deleted file mode 100644 index 3232edc6..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Copyright (C) 2013 Andreas Stuetz - * - * 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 de.blinkt.openvpn.views; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; -import android.database.DataSetObserver; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Typeface; -import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; -import androidx.core.view.ViewCompat; -import android.support.v4n.view.ViewPager; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Pair; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver.OnGlobalLayoutListener; -import android.widget.HorizontalScrollView; -import android.widget.LinearLayout; -import android.widget.TextView; - - -import java.util.Locale; - -import de.blinkt.openvpn.R; - -public class PagerSlidingTabStrip extends HorizontalScrollView implements TabBarView { - - private static final float OPAQUE = 1.0f; - private static final float HALF_TRANSP = 0.5f; - - public interface CustomTabProvider { - public View getCustomTabView(ViewGroup parent, int position); - } - - // @formatter:off - private static final int[] ATTRS = new int[]{ - android.R.attr.textSize, - android.R.attr.textColor, - android.R.attr.paddingLeft, - android.R.attr.paddingRight, - }; - // @formatter:on - - private final PagerAdapterObserver adapterObserver = new PagerAdapterObserver(); - - //These indexes must be related with the ATTR array above - private static final int TEXT_SIZE_INDEX = 0; - private static final int TEXT_COLOR_INDEX = 1; - private static final int PADDING_LEFT_INDEX = 2; - private static final int PADDING_RIGHT_INDEX = 3; - - private LinearLayout.LayoutParams defaultTabLayoutParams; - private LinearLayout.LayoutParams expandedTabLayoutParams; - - private final PageListener pageListener = new PageListener(); - public ViewPager.OnPageChangeListener delegatePageListener; - - private LinearLayout tabsContainer; - private ViewPager pager; - - private int tabCount; - - private int currentPosition = 0; - private float currentPositionOffset = 0f; - - private Paint rectPaint; - private Paint dividerPaint; - - private int indicatorColor; - private int indicatorHeight = 2; - - private int underlineHeight = 0; - private int underlineColor; - - private int dividerWidth = 0; - private int dividerPadding = 0; - private int dividerColor; - - private int tabPadding = 12; - private int tabTextSize = 14; - private ColorStateList tabTextColor = null; - private float tabTextAlpha = HALF_TRANSP; - private float tabTextSelectedAlpha = OPAQUE; - - private int paddingLeft = 0; - private int paddingRight = 0; - - private boolean shouldExpand = false; - private boolean textAllCaps = true; - private boolean isPaddingMiddle = false; - - private Typeface tabTypeface = null; - private int tabTypefaceStyle = Typeface.BOLD; - private int tabTypefaceSelectedStyle = Typeface.BOLD; - - private int scrollOffset; - private int lastScrollX = 0; - - private int tabBackgroundResId = R.drawable.slidingtab_background; - - private Locale locale; - - public PagerSlidingTabStrip(Context context) { - this(context, null); - } - - public PagerSlidingTabStrip(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setFillViewport(true); - setWillNotDraw(false); - tabsContainer = new LinearLayout(context); - tabsContainer.setOrientation(LinearLayout.HORIZONTAL); - tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - addView(tabsContainer); - - //Default color will be 'textColorPrimary' - int colorPrimary = context.getResources().getColor(android.R.color.primary_text_dark); - setTextColor(colorPrimary); - underlineColor = colorPrimary; - dividerColor = colorPrimary; - indicatorColor = colorPrimary; - - - DisplayMetrics dm = getResources().getDisplayMetrics(); - scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm); - indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm); - underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm); - dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm); - tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm); - dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm); - tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm); - - // get system attrs (android:textSize and android:textColor) - TypedArray a = context.obtainStyledAttributes(attrs, ATTRS); - tabTextSize = a.getDimensionPixelSize(TEXT_SIZE_INDEX, tabTextSize); - ColorStateList colorStateList = a.getColorStateList(TEXT_COLOR_INDEX); - if (colorStateList != null) { - tabTextColor = colorStateList; - } - paddingLeft = a.getDimensionPixelSize(PADDING_LEFT_INDEX, paddingLeft); - paddingRight = a.getDimensionPixelSize(PADDING_RIGHT_INDEX, paddingRight); - a.recycle(); - - //In case we have the padding they must be equal so we take the biggest - if (paddingRight < paddingLeft) { - paddingRight = paddingLeft; - } - - if (paddingLeft < paddingRight) { - paddingLeft = paddingRight; - } - - // get custom attrs - a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip); - indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, indicatorColor); - underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, underlineColor); - dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, dividerColor); - dividerWidth = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerWidth, dividerWidth); - indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, indicatorHeight); - underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsUnderlineHeight, underlineHeight); - dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerPadding, dividerPadding); - tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight, tabPadding); - tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_pstsTabBackground, tabBackgroundResId); - shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, shouldExpand); - scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsScrollOffset, scrollOffset); - textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps); - isPaddingMiddle = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsPaddingMiddle, isPaddingMiddle); - tabTypefaceStyle = a.getInt(R.styleable.PagerSlidingTabStrip_pstsTextStyle, Typeface.BOLD); - tabTypefaceSelectedStyle = a.getInt(R.styleable.PagerSlidingTabStrip_pstsTextSelectedStyle, Typeface.BOLD); - tabTextAlpha = a.getFloat(R.styleable.PagerSlidingTabStrip_pstsTextAlpha, HALF_TRANSP); - tabTextSelectedAlpha = a.getFloat(R.styleable.PagerSlidingTabStrip_pstsTextSelectedAlpha, OPAQUE); - a.recycle(); - - rectPaint = new Paint(); - rectPaint.setAntiAlias(true); - rectPaint.setStyle(Style.FILL); - - - dividerPaint = new Paint(); - dividerPaint.setAntiAlias(true); - dividerPaint.setStrokeWidth(dividerWidth); - - defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f); - - if (locale == null) { - locale = getResources().getConfiguration().locale; - } - } - - public void setViewPager(ViewPager pager) { - this.pager = pager; - if (pager.getAdapter() == null) { - throw new IllegalStateException("ViewPager does not have adapter instance."); - } - - pager.setOnPageChangeListener(pageListener); - pager.getAdapter().registerDataSetObserver(adapterObserver); - adapterObserver.setAttached(true); - notifyDataSetChanged(); - } - - public void notifyDataSetChanged() { - tabsContainer.removeAllViews(); - tabCount = pager.getAdapter().getCount(); - View tabView; - for (int i = 0; i < tabCount; i++) { - - if (pager.getAdapter() instanceof CustomTabProvider) { - tabView = ((CustomTabProvider) pager.getAdapter()).getCustomTabView(this, i); - } else { - tabView = LayoutInflater.from(getContext()).inflate(R.layout.padersliding_tab, this, false); - } - - CharSequence title = pager.getAdapter().getPageTitle(i); - - addTab(i, title, tabView); - } - - updateTabStyles(); - getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - - @SuppressWarnings("deprecation") - @SuppressLint("NewApi") - @Override - public void onGlobalLayout() { - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - getViewTreeObserver().removeGlobalOnLayoutListener(this); - } else { - getViewTreeObserver().removeOnGlobalLayoutListener(this); - } - - currentPosition = pager.getCurrentItem(); - currentPositionOffset = 0f; - scrollToChild(currentPosition, 0); - updateSelection(currentPosition); - } - }); - } - - private void addTab(final int position, CharSequence title, View tabView) { - TextView textView = (TextView) tabView.findViewById(R.id.tab_title); - if (textView != null) { - if (title != null) textView.setText(title); - float alpha = pager.getCurrentItem() == position ? tabTextSelectedAlpha : tabTextAlpha; - ViewCompat.setAlpha(textView, alpha); - } - - tabView.setFocusable(true); - tabView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (pager.getCurrentItem() != position) { - View tab = tabsContainer.getChildAt(pager.getCurrentItem()); - notSelected(tab); - pager.setCurrentItem(position); - } - } - }); - - tabView.setPadding(tabPadding, tabView.getPaddingTop(), tabPadding, tabView.getPaddingBottom()); - tabsContainer.addView(tabView, position, shouldExpand ? expandedTabLayoutParams : defaultTabLayoutParams); - } - - private void updateTabStyles() { - for (int i = 0; i < tabCount; i++) { - View v = tabsContainer.getChildAt(i); - v.setBackgroundResource(tabBackgroundResId); - TextView tab_title = (TextView) v.findViewById(R.id.tab_title); - - if (tab_title != null) { - tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize); - tab_title.setTypeface(tabTypeface, pager.getCurrentItem() == i ? tabTypefaceSelectedStyle : tabTypefaceStyle); - if (tabTextColor != null) { - tab_title.setTextColor(tabTextColor); - } - // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a - // pre-ICS-build - if (textAllCaps) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - tab_title.setAllCaps(true); - } else { - tab_title.setText(tab_title.getText().toString().toUpperCase(locale)); - } - } - } - } - - } - - private void scrollToChild(int position, int offset) { - if (tabCount == 0) { - return; - } - - int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset; - if (position > 0 || offset > 0) { - - //Half screen offset. - //- Either tabs start at the middle of the view scrolling straight away - //- Or tabs start at the begging (no padding) scrolling when indicator gets - // to the middle of the view width - newScrollX -= scrollOffset; - Pair lines = getIndicatorCoordinates(); - newScrollX += ((lines.second - lines.first) / 2); - } - - if (newScrollX != lastScrollX) { - lastScrollX = newScrollX; - scrollTo(newScrollX, 0); - } - } - - private Pair getIndicatorCoordinates() { - // default: line below current tab - View currentTab = tabsContainer.getChildAt(currentPosition); - float lineLeft = currentTab.getLeft(); - float lineRight = currentTab.getRight(); - - // if there is an offset, start interpolating left and right coordinates between current and next tab - if (currentPositionOffset > 0f && currentPosition < tabCount - 1) { - - View nextTab = tabsContainer.getChildAt(currentPosition + 1); - final float nextTabLeft = nextTab.getLeft(); - final float nextTabRight = nextTab.getRight(); - - lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft); - lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight); - } - return new Pair(lineLeft, lineRight); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (isInEditMode() || tabCount == 0) { - return; - } - - final int height = getHeight(); - // draw indicator line - rectPaint.setColor(indicatorColor); - Pair lines = getIndicatorCoordinates(); - canvas.drawRect(lines.first + paddingLeft, height - indicatorHeight, lines.second + paddingRight, height, rectPaint); - // draw underline - rectPaint.setColor(underlineColor); - canvas.drawRect(paddingLeft, height - underlineHeight, tabsContainer.getWidth() + paddingRight, height, rectPaint); - // draw divider - if (dividerWidth != 0) { - dividerPaint.setStrokeWidth(dividerWidth); - dividerPaint.setColor(dividerColor); - for (int i = 0; i < tabCount - 1; i++) { - View tab = tabsContainer.getChildAt(i); - canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint); - } - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - if (isPaddingMiddle) { - //Make sure tabContainer is bigger than the HorizontalScrollView to be able to scroll - tabsContainer.setMinimumWidth(getWidth()); - int halfFirstTab = 0; - if (tabsContainer.getChildCount() > 0) { - halfFirstTab = (tabsContainer.getChildAt(0).getWidth() / 2); - } - //The user choose the tabs to start in the middle of the view width (padding) - paddingLeft = paddingRight = getWidth() / 2 - halfFirstTab; - //Clipping padding to false to see the tabs while we pass them swiping - setClipToPadding(false); - } - - if (scrollOffset == 0) scrollOffset = getWidth() / 2 - paddingLeft; - setPadding(paddingLeft, getPaddingTop(), paddingRight, getPaddingBottom()); - super.onLayout(changed, l, t, r, b); - } - - public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { - this.delegatePageListener = listener; - } - - private class PageListener implements ViewPager.OnPageChangeListener { - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - currentPosition = position; - currentPositionOffset = positionOffset; - int offset = tabCount > 0 ? (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()) : 0; - scrollToChild(position, offset); - invalidate(); - if (delegatePageListener != null) { - delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - if (state == ViewPager.SCROLL_STATE_IDLE) { - scrollToChild(pager.getCurrentItem(), 0); - } - //Full alpha for current item - View currentTab = tabsContainer.getChildAt(pager.getCurrentItem()); - selected(currentTab); - //Half transparent for prev item - if (pager.getCurrentItem() - 1 >= 0) { - View prevTab = tabsContainer.getChildAt(pager.getCurrentItem() - 1); - notSelected(prevTab); - } - //Half transparent for next item - if (pager.getCurrentItem() + 1 <= pager.getAdapter().getCount() - 1) { - View nextTab = tabsContainer.getChildAt(pager.getCurrentItem() + 1); - notSelected(nextTab); - } - - if (delegatePageListener != null) { - delegatePageListener.onPageScrollStateChanged(state); - } - } - - @Override - public void onPageSelected(int position) { - updateSelection(position); - if (delegatePageListener != null) { - delegatePageListener.onPageSelected(position); - } - } - - } - - private void updateSelection(int position) { - for (int i = 0; i < tabCount; ++i) { - View tv = tabsContainer.getChildAt(i); - tv.setSelected(i == position); - } - } - - private void notSelected(View tab) { - TextView title = (TextView) tab.findViewById(R.id.tab_title); - if (title != null) { - title.setTypeface(tabTypeface, tabTypefaceStyle); - ViewCompat.setAlpha(title, tabTextAlpha); - } - } - - private void selected(View tab) { - TextView title = (TextView) tab.findViewById(R.id.tab_title); - if (title != null) { - title.setTypeface(tabTypeface, tabTypefaceSelectedStyle); - ViewCompat.setAlpha(title, tabTextSelectedAlpha); - } - } - - private class PagerAdapterObserver extends DataSetObserver { - - private boolean attached = false; - - @Override - public void onChanged() { - notifyDataSetChanged(); - } - - public void setAttached(boolean attached) { - this.attached = attached; - } - - public boolean isAttached() { - return attached; - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (pager != null) { - if (!adapterObserver.isAttached()) { - pager.getAdapter().registerDataSetObserver(adapterObserver); - adapterObserver.setAttached(true); - } - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (pager != null) { - if (adapterObserver.isAttached()) { - pager.getAdapter().unregisterDataSetObserver(adapterObserver); - adapterObserver.setAttached(false); - } - } - } - - @Override - public void onRestoreInstanceState(Parcelable state) { - SavedState savedState = (SavedState) state; - super.onRestoreInstanceState(savedState.getSuperState()); - currentPosition = savedState.currentPosition; - if (currentPosition != 0 && tabsContainer.getChildCount() > 0) { - notSelected(tabsContainer.getChildAt(0)); - selected(tabsContainer.getChildAt(currentPosition)); - } - requestLayout(); - } - - @Override - public Parcelable onSaveInstanceState() { - Parcelable superState = super.onSaveInstanceState(); - SavedState savedState = new SavedState(superState); - savedState.currentPosition = currentPosition; - return savedState; - } - - static class SavedState extends BaseSavedState { - int currentPosition; - - public SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - currentPosition = in.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(currentPosition); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } - - public int getIndicatorColor() { - return this.indicatorColor; - } - - public int getIndicatorHeight() { - return indicatorHeight; - } - - public int getUnderlineColor() { - return underlineColor; - } - - public int getDividerColor() { - return dividerColor; - } - - public int getDividerWidth() { - return dividerWidth; - } - - public int getUnderlineHeight() { - return underlineHeight; - } - - public int getDividerPadding() { - return dividerPadding; - } - - public int getScrollOffset() { - return scrollOffset; - } - - public boolean getShouldExpand() { - return shouldExpand; - } - - public int getTextSize() { - return tabTextSize; - } - - public boolean isTextAllCaps() { - return textAllCaps; - } - - public ColorStateList getTextColor() { - return tabTextColor; - } - - public int getTabBackground() { - return tabBackgroundResId; - } - - public int getTabPaddingLeftRight() { - return tabPadding; - } - - public void setIndicatorColor(int indicatorColor) { - this.indicatorColor = indicatorColor; - invalidate(); - } - - public void setIndicatorColorResource(int resId) { - this.indicatorColor = getResources().getColor(resId); - invalidate(); - } - - public void setIndicatorHeight(int indicatorLineHeightPx) { - this.indicatorHeight = indicatorLineHeightPx; - invalidate(); - } - - public void setUnderlineColor(int underlineColor) { - this.underlineColor = underlineColor; - invalidate(); - } - - public void setUnderlineColorResource(int resId) { - this.underlineColor = getResources().getColor(resId); - invalidate(); - } - - public void setDividerColor(int dividerColor) { - this.dividerColor = dividerColor; - invalidate(); - } - - public void setDividerColorResource(int resId) { - this.dividerColor = getResources().getColor(resId); - invalidate(); - } - - public void setDividerWidth(int dividerWidthPx) { - this.dividerWidth = dividerWidthPx; - invalidate(); - } - - public void setUnderlineHeight(int underlineHeightPx) { - this.underlineHeight = underlineHeightPx; - invalidate(); - } - - public void setDividerPadding(int dividerPaddingPx) { - this.dividerPadding = dividerPaddingPx; - invalidate(); - } - - public void setScrollOffset(int scrollOffsetPx) { - this.scrollOffset = scrollOffsetPx; - invalidate(); - } - - public void setShouldExpand(boolean shouldExpand) { - this.shouldExpand = shouldExpand; - if (pager != null) { - requestLayout(); - } - } - - public void setAllCaps(boolean textAllCaps) { - this.textAllCaps = textAllCaps; - } - - public void setTextSize(int textSizePx) { - this.tabTextSize = textSizePx; - updateTabStyles(); - } - - public void setTextColor(int textColor) { - setTextColor(new ColorStateList(new int[][]{new int[]{}}, new int[]{textColor})); - } - - public void setTextColor(ColorStateList colorStateList) { - this.tabTextColor = colorStateList; - updateTabStyles(); - } - - public void setTextColorResource(int resId) { - setTextColor(getResources().getColor(resId)); - } - - public void setTextColorStateListResource(int resId) { - setTextColor(getResources().getColorStateList(resId)); - } - - public void setTypeface(Typeface typeface, int style) { - this.tabTypeface = typeface; - this.tabTypefaceSelectedStyle = style; - updateTabStyles(); - } - - public void setTabBackground(int resId) { - this.tabBackgroundResId = resId; - } - - public void setTabPaddingLeftRight(int paddingPx) { - this.tabPadding = paddingPx; - updateTabStyles(); - } -} \ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java b/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java index 4b477f9c..46c9ed48 100644 --- a/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java +++ b/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java @@ -6,141 +6,55 @@ package de.blinkt.openvpn.views; import android.content.Context; -import android.preference.DialogPreference; import android.util.AttributeSet; -import android.util.Pair; -import android.view.View; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; + +import androidx.preference.DialogPreference; import de.blinkt.openvpn.R; -import de.blinkt.openvpn.VpnProfile; public class RemoteCNPreference extends DialogPreference { - private Spinner mSpinner; - private EditText mEditText; - private int mDNType; - private String mDn; - private TextView mRemoteTLSNote; - //private ScrollView mScrollView; - - public RemoteCNPreference(Context context, AttributeSet attrs) { - super(context, attrs); - setDialogLayoutResource(R.layout.tlsremote); - - } - - @Override - protected void onBindDialogView(View view) { - - super.onBindDialogView(view); - - mEditText = (EditText) view.findViewById(R.id.tlsremotecn); - mSpinner = (Spinner) view.findViewById(R.id.x509verifytype); - mRemoteTLSNote = (TextView) view.findViewById(R.id.tlsremotenote); - //mScrollView = (ScrollView) view.findViewById(R.id.tlsremotescroll); - if(mDn!=null) - mEditText.setText(mDn); - - populateSpinner(); - - } - - - - public String getCNText() { - return mDn; - } - - public int getAuthtype() { - return mDNType; - } - - public void setDN(String dn) { - mDn = dn; - if(mEditText!=null) - mEditText.setText(dn); - } - - public void setAuthType(int x509authtype) { - mDNType = x509authtype; - if (mSpinner!=null) - populateSpinner(); - } - - @Override - protected void onDialogClosed(boolean positiveResult) { - super.onDialogClosed(positiveResult); - - if (positiveResult) { - String dn = mEditText.getText().toString(); - int authtype = getAuthTypeFromSpinner(); - if (callChangeListener(new Pair(authtype, dn))) { - mDn = dn; - mDNType = authtype; - } - } - } - - private void populateSpinner() { - ArrayAdapter authtypes = new ArrayAdapter(getContext(), android.R.layout.simple_spinner_item); - authtypes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - - authtypes.add(getContext().getString(R.string.complete_dn)); - authtypes.add(getContext().getString(R.string.rdn)); - authtypes.add(getContext().getString(R.string.rdn_prefix)); - if ((mDNType == VpnProfile.X509_VERIFY_TLSREMOTE || mDNType == VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING) - && !(mDn==null || "".equals(mDn))) { - authtypes.add(getContext().getString(R.string.tls_remote_deprecated)); - mRemoteTLSNote.setVisibility(View.VISIBLE); - } else { - mRemoteTLSNote.setVisibility(View.GONE); - } - mSpinner.setAdapter(authtypes); - mSpinner.setSelection(getSpinnerPositionFromAuthTYPE()); - } - - private int getSpinnerPositionFromAuthTYPE() { - switch (mDNType) { - case VpnProfile.X509_VERIFY_TLSREMOTE_DN: - return 0; - case VpnProfile.X509_VERIFY_TLSREMOTE_RDN: - return 1; - case VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX: - return 2; - case VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING: - case VpnProfile.X509_VERIFY_TLSREMOTE: - if (mDn==null || "".equals(mDn)) - return 1; - else - return 3; - - - default: - return 0; - } - } - - private int getAuthTypeFromSpinner() { - int pos = mSpinner.getSelectedItemPosition(); - switch (pos) { - case 0: - return VpnProfile.X509_VERIFY_TLSREMOTE_DN; - case 1: - return VpnProfile.X509_VERIFY_TLSREMOTE_RDN; - case 2: - return VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX; - case 3: - // This is the tls-remote entry, only visible if mDntype is a - // tls-remote type - return mDNType; - default: - return VpnProfile.X509_VERIFY_TLSREMOTE; - } - } + private int mDNType; + private String mDn; + + public RemoteCNPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + } + + public RemoteCNPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, defStyleAttr); + } + + public RemoteCNPreference(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RemoteCNPreference(Context context) { + this(context, null); + } + + + public void setDN(String dn) { + mDn = dn; + } + + + public void setAuthType(int x509authtype) { + mDNType = x509authtype; + } + + public String getCNText() { + return mDn; + } + + public int getAuthtype() { + return mDNType; + } + @Override + public int getDialogLayoutResource() { + return R.layout.tlsremote; + } } diff --git a/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreferenceDialog.java b/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreferenceDialog.java new file mode 100644 index 00000000..37258489 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreferenceDialog.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012-2019 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ + +package de.blinkt.openvpn.views; + +import android.os.Bundle; +import android.util.Pair; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.preference.PreferenceDialogFragmentCompat; + +import de.blinkt.openvpn.R; +import de.blinkt.openvpn.VpnProfile; + +import static de.blinkt.openvpn.VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING; + +public class RemoteCNPreferenceDialog extends PreferenceDialogFragmentCompat { + private Spinner mSpinner; + private EditText mEditText; + private TextView mRemoteTLSNote; + + public static RemoteCNPreferenceDialog newInstance(String key) { + RemoteCNPreferenceDialog f = new RemoteCNPreferenceDialog(); + Bundle args = new Bundle(); + args.putString(ARG_KEY, key); + f.setArguments(args); + return f; + } + + @Override + public void onBindDialogView(View view) { + String mDn = ((RemoteCNPreference) getPreference()).getCNText(); + int mDNType = ((RemoteCNPreference) getPreference()).getAuthtype(); + + mEditText = view.findViewById(R.id.tlsremotecn); + mSpinner = view.findViewById(R.id.x509verifytype); + mRemoteTLSNote = view.findViewById(R.id.tlsremotenote); + mEditText.setText(mDn); + + populateSpinner(mDn, mDNType); + } + + private void populateSpinner(String mDn, int mDNType) { + ArrayAdapter authtypes = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item); + authtypes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + authtypes.add(requireContext().getString(R.string.complete_dn)); + authtypes.add(requireContext().getString(R.string.rdn)); + authtypes.add(requireContext().getString(R.string.rdn_prefix)); + if ((mDNType == VpnProfile.X509_VERIFY_TLSREMOTE || mDNType == X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING) + && !(mDn == null || "".equals(mDn))) { + authtypes.add(requireContext().getString(R.string.tls_remote_deprecated)); + mRemoteTLSNote.setVisibility(View.VISIBLE); + } else { + mRemoteTLSNote.setVisibility(View.GONE); + } + mSpinner.setAdapter(authtypes); + mSpinner.setSelection(getSpinnerPositionFromAuthTYPE(mDNType, mDn)); + } + + private int getSpinnerPositionFromAuthTYPE(int mDNType, String mDn) { + switch (mDNType) { + case VpnProfile.X509_VERIFY_TLSREMOTE_DN: + return 0; + case VpnProfile.X509_VERIFY_TLSREMOTE_RDN: + return 1; + case VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX: + return 2; + case X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING: + case VpnProfile.X509_VERIFY_TLSREMOTE: + if (mDn == null || "".equals(mDn)) + return 1; + else + return 3; + + + default: + return 0; + } + } + + + private int getAuthTypeFromSpinner() { + int pos = mSpinner.getSelectedItemPosition(); + switch (pos) { + case 0: + return VpnProfile.X509_VERIFY_TLSREMOTE_DN; + case 1: + return VpnProfile.X509_VERIFY_TLSREMOTE_RDN; + case 2: + return VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX; + case 3: + // This is the tls-remote entry, only visible if mDntype is a + // tls-remote type + return X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING; + default: + return VpnProfile.X509_VERIFY_TLSREMOTE; + } + } + + + @Override + public void onDialogClosed(boolean positiveResult) { + + if (positiveResult) { + RemoteCNPreference pref = ((RemoteCNPreference) getPreference()); + + String dn = mEditText.getText().toString(); + int authtype = getAuthTypeFromSpinner(); + if (pref.callChangeListener(new Pair<>(authtype, dn))) { + pref.setDN(dn); + pref.setAuthType(authtype); + } + } + } + + +} diff --git a/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java index 481e4c16..4f1150a2 100644 --- a/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java +++ b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java @@ -5,20 +5,22 @@ package de.blinkt.openvpn.views; -import android.app.Fragment; -import android.app.FragmentManager; import android.content.Context; import android.content.res.Resources; import android.os.Bundle; + +import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import android.support.v4n.app.FragmentStatePagerAdapter; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; import java.util.Vector; /** * Created by arne on 18.11.14. */ -public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { +public class ScreenSlidePagerAdapter extends FragmentPagerAdapter { private final Resources res; private Bundle mFragmentArguments; @@ -46,6 +48,7 @@ public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { res = c.getResources(); } + @NonNull @Override public Fragment getItem(int position) { try { diff --git a/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java b/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java deleted file mode 100644 index 51ad6933..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2013 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 de.blinkt.openvpn.views; - -import android.content.Context; -import android.graphics.Typeface; -import android.os.Build; -import android.support.v4n.view.PagerAdapter; -import android.support.v4n.view.ViewPager; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.HorizontalScrollView; -import android.widget.TextView; - -/** - * To be used with ViewPager to provide a tab indicator component which give constant feedback as to - * the user's scroll progress. - *

- * To use the component, simply add it to your view hierarchy. Then in your - * {@link android.app.Activity} or {@link androidx.core.app.Fragment} call - * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. - *

- * The colors can be customized in two ways. The first and simplest is to provide an array of colors - * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The - * alternative is via the {@link TabColorizer} interface which provides you complete control over - * which color is used for any individual position. - *

- * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, - * providing the layout ID of your custom layout. - */ -public class SlidingTabLayout extends HorizontalScrollView implements TabBarView { - - /** - * Allows complete control over the colors drawn in the tab layout. Set with - * {@link #setCustomTabColorizer(TabColorizer)}. - */ - public interface TabColorizer { - - /** - * @return return the color of the indicator used when {@code position} is selected. - */ - int getIndicatorColor(int position); - - /** - * @return return the color of the divider drawn to the right of {@code position}. - */ - int getDividerColor(int position); - - } - - private static final int TITLE_OFFSET_DIPS = 24; - private static final int TAB_VIEW_PADDING_DIPS = 16; - private static final int TAB_VIEW_TEXT_SIZE_SP = 12; - - private int mTitleOffset; - - private int mTabViewLayoutId; - private int mTabViewTextViewId; - - private ViewPager mViewPager; - private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; - - private final SlidingTabStrip mTabStrip; - - public SlidingTabLayout(Context context) { - this(context, null); - } - - public SlidingTabLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - // Disable the Scroll Bar - setHorizontalScrollBarEnabled(false); - // Make sure that the Tab Strips fills this View - setFillViewport(true); - - mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); - - mTabStrip = new SlidingTabStrip(context); - addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - } - - /** - * Set the custom {@link TabColorizer} to be used. - * - * If you only require simple custmisation then you can use - * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve - * similar effects. - */ - public void setCustomTabColorizer(TabColorizer tabColorizer) { - mTabStrip.setCustomTabColorizer(tabColorizer); - } - - /** - * Sets the colors to be used for indicating the selected tab. These colors are treated as a - * circular array. Providing one color will mean that all tabs are indicated with the same color. - */ - public void setSelectedIndicatorColors(int... colors) { - mTabStrip.setSelectedIndicatorColors(colors); - } - - /** - * Sets the colors to be used for tab dividers. These colors are treated as a circular array. - * Providing one color will mean that all tabs are indicated with the same color. - */ - public void setDividerColors(int... colors) { - mTabStrip.setDividerColors(colors); - } - - /** - * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are - * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so - * that the layout can update it's scroll position correctly. - * - * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) - */ - public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { - mViewPagerPageChangeListener = listener; - } - - /** - * Set the custom layout to be inflated for the tab views. - * - * @param layoutResId Layout id to be inflated - * @param textViewId id of the {@link TextView} in the inflated view - */ - public void setCustomTabView(int layoutResId, int textViewId) { - mTabViewLayoutId = layoutResId; - mTabViewTextViewId = textViewId; - } - - /** - * Sets the associated view pager. Note that the assumption here is that the pager content - * (number of tabs and tab titles) does not change after this call has been made. - */ - public void setViewPager(ViewPager viewPager) { - mTabStrip.removeAllViews(); - - mViewPager = viewPager; - if (viewPager != null) { - viewPager.setOnPageChangeListener(new InternalViewPagerListener()); - populateTabStrip(); - } - } - - /** - * Create a default view to be used for tabs. This is called if a custom tab view is not set via - * {@link #setCustomTabView(int, int)}. - */ - protected TextView createDefaultTabView(Context context) { - TextView textView = new TextView(context); - textView.setGravity(Gravity.CENTER); - textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); - textView.setTypeface(Typeface.DEFAULT_BOLD); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - // If we're running on Honeycomb or newer, then we can use the Theme's - // selectableItemBackground to ensure that the View has a pressed state - TypedValue outValue = new TypedValue(); - getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, - outValue, true); - textView.setBackgroundResource(outValue.resourceId); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style - textView.setAllCaps(true); - } - - int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); - textView.setPadding(padding, padding, padding, padding); - - return textView; - } - - private void populateTabStrip() { - final PagerAdapter adapter = mViewPager.getAdapter(); - final View.OnClickListener tabClickListener = new TabClickListener(); - - for (int i = 0; i < adapter.getCount(); i++) { - View tabView = null; - TextView tabTitleView = null; - - if (mTabViewLayoutId != 0) { - // If there is a custom tab view layout id set, try and inflate it - tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, - false); - tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); - } - - if (tabView == null) { - tabView = createDefaultTabView(getContext()); - } - - if (tabTitleView == null && TextView.class.isInstance(tabView)) { - tabTitleView = (TextView) tabView; - } - - tabTitleView.setText(adapter.getPageTitle(i)); - tabView.setOnClickListener(tabClickListener); - - mTabStrip.addView(tabView); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mViewPager != null) { - scrollToTab(mViewPager.getCurrentItem(), 0); - } - } - - private void scrollToTab(int tabIndex, int positionOffset) { - final int tabStripChildCount = mTabStrip.getChildCount(); - if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { - return; - } - - View selectedChild = mTabStrip.getChildAt(tabIndex); - if (selectedChild != null) { - int targetScrollX = selectedChild.getLeft() + positionOffset; - - if (tabIndex > 0 || positionOffset > 0) { - // If we're not at the first child and are mid-scroll, make sure we obey the offset - targetScrollX -= mTitleOffset; - } - - scrollTo(targetScrollX, 0); - } - } - - private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { - private int mScrollState; - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - int tabStripChildCount = mTabStrip.getChildCount(); - if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { - return; - } - - mTabStrip.onViewPagerPageChanged(position, positionOffset); - - View selectedTitle = mTabStrip.getChildAt(position); - int extraOffset = (selectedTitle != null) - ? (int) (positionOffset * selectedTitle.getWidth()) - : 0; - scrollToTab(position, extraOffset); - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, - positionOffsetPixels); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - mScrollState = state; - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrollStateChanged(state); - } - } - - @Override - public void onPageSelected(int position) { - if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { - mTabStrip.onViewPagerPageChanged(position, 0f); - scrollToTab(position, 0); - } - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageSelected(position); - } - } - - } - - private class TabClickListener implements View.OnClickListener { - @Override - public void onClick(View v) { - for (int i = 0; i < mTabStrip.getChildCount(); i++) { - if (v == mTabStrip.getChildAt(i)) { - mViewPager.setCurrentItem(i); - return; - } - } - } - } - -} diff --git a/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java b/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java deleted file mode 100644 index 88bfb9a3..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2013 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 de.blinkt.openvpn.views; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.View; -import android.widget.LinearLayout; - -class SlidingTabStrip extends LinearLayout { - - private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; - private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; - private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; - private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; - - private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; - private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; - private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; - - private final int mBottomBorderThickness; - private final Paint mBottomBorderPaint; - - private final int mSelectedIndicatorThickness; - private final Paint mSelectedIndicatorPaint; - - private final int mDefaultBottomBorderColor; - - private final Paint mDividerPaint; - private final float mDividerHeight; - - private int mSelectedPosition; - private float mSelectionOffset; - - private SlidingTabLayout.TabColorizer mCustomTabColorizer; - private final SimpleTabColorizer mDefaultTabColorizer; - - SlidingTabStrip(Context context) { - this(context, null); - } - - SlidingTabStrip(Context context, AttributeSet attrs) { - super(context, attrs); - setWillNotDraw(false); - - final float density = getResources().getDisplayMetrics().density; - - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true); - final int themeForegroundColor = outValue.data; - - mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, - DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); - - mDefaultTabColorizer = new SimpleTabColorizer(); - mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); - mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, - DEFAULT_DIVIDER_COLOR_ALPHA)); - - mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); - mBottomBorderPaint = new Paint(); - mBottomBorderPaint.setColor(mDefaultBottomBorderColor); - - mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); - mSelectedIndicatorPaint = new Paint(); - - mDividerHeight = DEFAULT_DIVIDER_HEIGHT; - mDividerPaint = new Paint(); - mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); - } - - void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { - mCustomTabColorizer = customTabColorizer; - invalidate(); - } - - void setSelectedIndicatorColors(int... colors) { - // Make sure that the custom colorizer is removed - mCustomTabColorizer = null; - mDefaultTabColorizer.setIndicatorColors(colors); - invalidate(); - } - - void setDividerColors(int... colors) { - // Make sure that the custom colorizer is removed - mCustomTabColorizer = null; - mDefaultTabColorizer.setDividerColors(colors); - invalidate(); - } - - void onViewPagerPageChanged(int position, float positionOffset) { - mSelectedPosition = position; - mSelectionOffset = positionOffset; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - final int height = getHeight(); - final int childCount = getChildCount(); - final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); - final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null - ? mCustomTabColorizer - : mDefaultTabColorizer; - - // Thick colored underline below the current selection - if (childCount > 0) { - View selectedTitle = getChildAt(mSelectedPosition); - int left = selectedTitle.getLeft(); - int right = selectedTitle.getRight(); - int color = tabColorizer.getIndicatorColor(mSelectedPosition); - - if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { - int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); - if (color != nextColor) { - color = blendColors(nextColor, color, mSelectionOffset); - } - - // Draw the selection partway between the tabs - View nextTitle = getChildAt(mSelectedPosition + 1); - left = (int) (mSelectionOffset * nextTitle.getLeft() + - (1.0f - mSelectionOffset) * left); - right = (int) (mSelectionOffset * nextTitle.getRight() + - (1.0f - mSelectionOffset) * right); - } - - mSelectedIndicatorPaint.setColor(color); - - canvas.drawRect(left, height - mSelectedIndicatorThickness, right, - height, mSelectedIndicatorPaint); - } - - // Thin underline along the entire bottom edge - canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); - - // Vertical separators between the titles - int separatorTop = (height - dividerHeightPx) / 2; - for (int i = 0; i < childCount - 1; i++) { - View child = getChildAt(i); - mDividerPaint.setColor(tabColorizer.getDividerColor(i)); - canvas.drawLine(child.getRight(), separatorTop, child.getRight(), - separatorTop + dividerHeightPx, mDividerPaint); - } - } - - /** - * Set the alpha value of the {@code color} to be the given {@code alpha} value. - */ - private static int setColorAlpha(int color, byte alpha) { - return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); - } - - /** - * Blend {@code color1} and {@code color2} using the given ratio. - * - * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, - * 0.0 will return {@code color2}. - */ - private static int blendColors(int color1, int color2, float ratio) { - final float inverseRation = 1f - ratio; - float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); - float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); - float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); - return Color.rgb((int) r, (int) g, (int) b); - } - - private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { - private int[] mIndicatorColors; - private int[] mDividerColors; - - @Override - public final int getIndicatorColor(int position) { - return mIndicatorColors[position % mIndicatorColors.length]; - } - - @Override - public final int getDividerColor(int position) { - return mDividerColors[position % mDividerColors.length]; - } - - void setIndicatorColors(int... colors) { - mIndicatorColors = colors; - } - - void setDividerColors(int... colors) { - mDividerColors = colors; - } - } -} \ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java b/main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java deleted file mode 100644 index 71f03c03..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2012-2016 Arne Schwabe - * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt - */ - -package de.blinkt.openvpn.views; - -import android.support.v4n.view.ViewPager; - -/** - * Created by arne on 18.11.14. - */ -public interface TabBarView { - - void setViewPager(ViewPager mPager); -} -- cgit v1.2.3