Reduce flakiness of our dependencies.

main
Greyson Parrelli 2022-10-21 11:04:40 -04:00 zatwierdzone przez Alex Hart
rodzic fb8b230442
commit 1007b4d635
48 zmienionych plików z 3761 dodań i 402 usunięć

Wyświetl plik

@ -16,9 +16,6 @@ repositories {
includeGroupByRegex "org\\.signal.*"
}
}
maven {
url "https://www.jitpack.io"
}
google()
mavenCentral()
@ -29,10 +26,6 @@ repositories {
jcenter {
content {
includeVersion "mobi.upod", "time-duration-picker", "1.1.3"
includeVersion "cn.carbswang.android", "NumberPickerView", "1.0.9"
includeVersion "com.takisoft.fix", "colorpicker", "0.9.1"
includeVersion "com.codewaves.stickyheadergrid", "stickyheadergrid", "0.9.4"
includeVersion "com.google.android", "flexbox", "0.3.0"
}
}
}
@ -474,6 +467,8 @@ dependencies {
implementation project(':contacts')
implementation project(':qr')
implementation project(':sms-exporter')
implementation project(':sticky-header-grid')
implementation project(':photoview')
implementation libs.libsignal.android
implementation libs.google.protobuf.javalite
@ -494,7 +489,6 @@ dependencies {
implementation libs.emilsjolander.stickylistheaders
implementation libs.jpardogo.materialtabstrip
implementation libs.apache.httpclient.android
implementation libs.photoview
implementation libs.glide.glide
implementation libs.roundedimageview
implementation libs.materialish.progress
@ -503,12 +497,10 @@ dependencies {
implementation libs.google.zxing.android.integration
implementation libs.time.duration.picker
implementation libs.google.zxing.core
implementation libs.google.flexbox
implementation (libs.subsampling.scale.image.view) {
exclude group: 'com.android.support', module: 'support-annotations'
}
implementation (libs.numberpickerview) {
exclude group: 'com.android.support', module: 'appcompat-v7'
}
implementation (libs.android.tooltips) {
exclude group: 'com.android.support', module: 'appcompat-v7'
}
@ -517,15 +509,9 @@ dependencies {
exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection'
}
implementation libs.stream
implementation (libs.colorpicker) {
exclude group: 'com.android.support', module: 'appcompat-v7'
exclude group: 'com.android.support', module: 'recyclerview-v7'
}
implementation libs.lottie
implementation libs.stickyheadergrid
implementation libs.signal.android.database.sqlcipher
implementation libs.androidx.sqlite

Wyświetl plik

@ -87,7 +87,7 @@ public class ConfirmKbsPinFragment extends BaseKbsPinFragment<ConfirmKbsPinViewM
getLabel().setText(R.string.ConfirmKbsPinFragment__re_enter_your_pin);
break;
case PIN_DOES_NOT_MATCH:
getLabel().setText(SpanUtil.color(ContextCompat.getColor(requireContext(), R.color.red),
getLabel().setText(SpanUtil.color(ContextCompat.getColor(requireContext(), R.color.red_500),
getString(R.string.ConfirmKbsPinFragment__pins_dont_match)));
getInput().getText().clear();
break;

Wyświetl plik

@ -54,7 +54,7 @@ public class CreateKbsPinFragment extends BaseKbsPinFragment<CreateKbsPinViewMod
viewModel.getNavigationEvents().observe(getViewLifecycleOwner(), e -> onConfirmPin(e.getUserEntry(), e.getKeyboard(), args.getIsPinChange()));
viewModel.getErrorEvents().observe(getViewLifecycleOwner(), e -> {
if (e == CreateKbsPinViewModel.PinErrorEvent.WEAK_PIN) {
getLabel().setText(SpanUtil.color(ContextCompat.getColor(requireContext(), R.color.red),
getLabel().setText(SpanUtil.color(ContextCompat.getColor(requireContext(), R.color.red_500),
getString(R.string.CreateKbsPinFragment__choose_a_stronger_pin)));
shake(getInput(), () -> getInput().getText().clear());
} else {

Wyświetl plik

@ -18,8 +18,6 @@ import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.CustomDefaultPreference;
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference;
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreferenceDialogFragmentCompat;
public abstract class CorrectedPreferenceFragment extends PreferenceFragmentCompat {
@ -40,9 +38,7 @@ public abstract class CorrectedPreferenceFragment extends PreferenceFragmentComp
public void onDisplayPreferenceDialog(Preference preference) {
DialogFragment dialogFragment = null;
if (preference instanceof ColorPickerPreference) {
dialogFragment = ColorPickerPreferenceDialogFragmentCompat.newInstance(preference.getKey());
} else if (preference instanceof CustomDefaultPreference) {
if (preference instanceof CustomDefaultPreference) {
dialogFragment = CustomDefaultPreference.CustomDefaultPreferenceDialogFragmentCompat.newInstance(preference.getKey());
}

Wyświetl plik

@ -1,253 +0,0 @@
package org.thoughtcrime.securesms.preferences.widgets;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.DialogPreference;
import androidx.preference.PreferenceViewHolder;
import com.takisoft.colorpicker.ColorPickerDialog;
import com.takisoft.colorpicker.ColorPickerDialog.Size;
import com.takisoft.colorpicker.ColorStateDrawable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
public class ColorPickerPreference extends DialogPreference {
private static final String TAG = Log.tag(ColorPickerPreference.class);
private int[] colors;
private CharSequence[] colorDescriptions;
private int color;
private int columns;
private int size;
private boolean sortColors;
private ImageView colorWidget;
private OnPreferenceChangeListener listener;
public ColorPickerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorPickerPreference, defStyleAttr, 0);
int colorsId = a.getResourceId(R.styleable.ColorPickerPreference_colors, R.array.color_picker_default_colors);
if (colorsId != 0) {
colors = context.getResources().getIntArray(colorsId);
}
colorDescriptions = a.getTextArray(R.styleable.ColorPickerPreference_colorDescriptions);
color = a.getColor(R.styleable.ColorPickerPreference_currentColor, 0);
columns = a.getInt(R.styleable.ColorPickerPreference_columns, 3);
size = a.getInt(R.styleable.ColorPickerPreference_colorSize, 2);
sortColors = a.getBoolean(R.styleable.ColorPickerPreference_sortColors, false);
a.recycle();
setWidgetLayoutResource(R.layout.preference_widget_color_swatch);
}
public ColorPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@SuppressLint("RestrictedApi")
public ColorPickerPreference(Context context, AttributeSet attrs) {
this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
android.R.attr.dialogPreferenceStyle));
}
public ColorPickerPreference(Context context) {
this(context, null);
}
@Override
public void setOnPreferenceChangeListener(OnPreferenceChangeListener listener) {
super.setOnPreferenceChangeListener(listener);
this.listener = listener;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
colorWidget = (ImageView) holder.findViewById(R.id.color_picker_widget);
setColorOnWidget(color);
}
private void setColorOnWidget(int color) {
if (colorWidget == null) {
return;
}
Drawable[] colorDrawable = new Drawable[]
{ContextCompat.getDrawable(getContext(), R.drawable.colorpickerpreference_pref_swatch)};
colorWidget.setImageDrawable(new ColorStateDrawable(colorDrawable, color));
}
/**
* Returns the current color.
*
* @return The current color.
*/
public int getColor() {
return color;
}
/**
* Sets the current color.
*
* @param color The current color.
*/
public void setColor(int color) {
setInternalColor(color, false);
}
/**
* Returns all of the available colors.
*
* @return The available colors.
*/
public int[] getColors() {
return colors;
}
/**
* Sets the available colors.
*
* @param colors The available colors.
*/
public void setColors(int[] colors) {
this.colors = colors;
}
/**
* Returns whether the available colors should be sorted automatically based on their HSV
* values.
*
* @return Whether the available colors should be sorted automatically based on their HSV
* values.
*/
public boolean isSortColors() {
return sortColors;
}
/**
* Sets whether the available colors should be sorted automatically based on their HSV
* values. The sorting does not modify the order of the original colors supplied via
* {@link #setColors(int[])} or the XML attribute {@code app:colors}.
*
* @param sortColors Whether the available colors should be sorted automatically based on their
* HSV values.
*/
public void setSortColors(boolean sortColors) {
this.sortColors = sortColors;
}
/**
* Returns the available colors' descriptions that can be used by accessibility services.
*
* @return The available colors' descriptions.
*/
public CharSequence[] getColorDescriptions() {
return colorDescriptions;
}
/**
* Sets the available colors' descriptions that can be used by accessibility services.
*
* @param colorDescriptions The available colors' descriptions.
*/
public void setColorDescriptions(CharSequence[] colorDescriptions) {
this.colorDescriptions = colorDescriptions;
}
/**
* Returns the number of columns to be used in the picker dialog for displaying the available
* colors. If the value is less than or equals to 0, the number of columns will be determined
* automatically by the system using FlexboxLayoutManager.
*
* @return The number of columns to be used in the picker dialog.
* @see com.google.android.flexbox.FlexboxLayoutManager
*/
public int getColumns() {
return columns;
}
/**
* Sets the number of columns to be used in the picker dialog for displaying the available
* colors. If the value is less than or equals to 0, the number of columns will be determined
* automatically by the system using FlexboxLayoutManager.
*
* @param columns The number of columns to be used in the picker dialog. Use 0 to set it to
* 'auto' mode.
* @see com.google.android.flexbox.FlexboxLayoutManager
*/
public void setColumns(int columns) {
this.columns = columns;
}
/**
* Returns the size of the color swatches in the dialog. It can be either
* {@link ColorPickerDialog#SIZE_SMALL} or {@link ColorPickerDialog#SIZE_LARGE}.
*
* @return The size of the color swatches in the dialog.
* @see ColorPickerDialog#SIZE_SMALL
* @see ColorPickerDialog#SIZE_LARGE
*/
@Size
public int getSize() {
return size;
}
/**
* Sets the size of the color swatches in the dialog. It can be either
* {@link ColorPickerDialog#SIZE_SMALL} or {@link ColorPickerDialog#SIZE_LARGE}.
*
* @param size The size of the color swatches in the dialog. It can be either
* {@link ColorPickerDialog#SIZE_SMALL} or {@link ColorPickerDialog#SIZE_LARGE}.
* @see ColorPickerDialog#SIZE_SMALL
* @see ColorPickerDialog#SIZE_LARGE
*/
public void setSize(@Size int size) {
this.size = size;
}
private void setInternalColor(int color, boolean force) {
int oldColor = getPersistedInt(0);
boolean changed = oldColor != color;
if (changed || force) {
this.color = color;
persistInt(color);
setColorOnWidget(color);
if (listener != null) listener.onPreferenceChange(this, color);
notifyChanged();
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValueObj) {
final String defaultValue = (String) defaultValueObj;
setInternalColor(restoreValue ? getPersistedInt(0) : (!TextUtils.isEmpty(defaultValue) ? Color.parseColor(defaultValue) : 0), true);
}
}

Wyświetl plik

@ -1,65 +0,0 @@
package org.thoughtcrime.securesms.preferences.widgets;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceDialogFragmentCompat;
import com.takisoft.colorpicker.ColorPickerDialog;
import com.takisoft.colorpicker.OnColorSelectedListener;
public class ColorPickerPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat implements OnColorSelectedListener {
private int pickedColor;
public static ColorPickerPreferenceDialogFragmentCompat newInstance(String key) {
ColorPickerPreferenceDialogFragmentCompat fragment = new ColorPickerPreferenceDialogFragmentCompat();
Bundle b = new Bundle(1);
b.putString(PreferenceDialogFragmentCompat.ARG_KEY, key);
fragment.setArguments(b);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
ColorPickerPreference pref = getColorPickerPreference();
ColorPickerDialog.Params params = new ColorPickerDialog.Params.Builder(getContext())
.setSelectedColor(pref.getColor())
.setColors(pref.getColors())
.setColorContentDescriptions(pref.getColorDescriptions())
.setSize(pref.getSize())
.setSortColors(pref.isSortColors())
.setColumns(pref.getColumns())
.build();
ColorPickerDialog dialog = new ColorPickerDialog(getActivity(), this, params);
dialog.setTitle(pref.getDialogTitle());
return dialog;
}
@Override
public void onDialogClosed(boolean positiveResult) {
ColorPickerPreference preference = getColorPickerPreference();
if (positiveResult) {
preference.setColor(pickedColor);
}
}
@Override
public void onColorSelected(int color) {
this.pickedColor = color;
super.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
}
ColorPickerPreference getColorPickerPreference() {
return (ColorPickerPreference) getPreference();
}
}

Wyświetl plik

@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.net.ContentProxySelector
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.DeviceProperties
import org.thoughtcrime.securesms.util.FeatureFlags
import kotlin.time.Duration.Companion.seconds
/**

Wyświetl plik

@ -2,7 +2,7 @@
<ripple android:color="@color/green_700" xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="oval">
<solid android:color="@color/green" />
<solid android:color="@color/green_500" />
</shape>
</item>
</ripple>

Wyświetl plik

@ -2,7 +2,7 @@
<ripple android:color="@color/red_700" xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="oval">
<solid android:color="@color/red" />
<solid android:color="@color/red_500" />
</shape>
</item>
</ripple>

Wyświetl plik

@ -7,7 +7,7 @@
</item>
<item android:state_pressed="false">
<shape android:shape="oval">
<solid android:color="@color/green" />
<solid android:color="@color/green_500" />
</shape>
</item>
</selector>

Wyświetl plik

@ -7,7 +7,7 @@
</item>
<item android:state_pressed="false">
<shape android:shape="oval">
<solid android:color="@color/red" />
<solid android:color="@color/red_500" />
</shape>
</item>
</selector>

Wyświetl plik

@ -5,7 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:background="@color/green"
tools:background="@color/green_500"
tools:layout_width="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">

Wyświetl plik

@ -7,7 +7,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical|end"
android:orientation="horizontal"
tools:background="@color/green"
tools:background="@color/green_500"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<androidx.appcompat.widget.AppCompatTextView

Wyświetl plik

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center">
<cn.carbswang.android.numberpickerview.library.NumberPickerView
android:id="@+id/expiration_number_picker"
android:layout_alignParentTop="true"
app:npv_WrapSelectorWheel="false"
app:npv_DividerColor="#cbc8ea"
app:npv_TextColorNormal="@color/signal_text_primary_dialog"
app:npv_TextColorSelected="@color/signal_text_selected"
app:npv_ItemPaddingVertical="20dp"
app:npv_TextColorHint="@color/grey_600"
app:npv_TextSizeNormal="16sp"
app:npv_TextSizeSelected="16sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView android:id="@+id/expiration_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/expiration_number_picker"
android:minLines="3"
android:padding="20dp"
tools:text="Your messages will not expire."/>
</RelativeLayout>

Wyświetl plik

@ -18,6 +18,6 @@
android:layout_height="16dp"
android:src="@drawable/circle_white"
android:layout_gravity="center"
tools:tint="@color/red"/>
tools:tint="@color/red_500"/>
</FrameLayout>

Wyświetl plik

@ -42,7 +42,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/red" />
tools:background="@color/red_500" />
<TextView
android:id="@+id/text_switch"
@ -61,7 +61,7 @@
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/camera_switch"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/green" />
tools:background="@color/green_500" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

Wyświetl plik

@ -33,7 +33,7 @@
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/red" />
tools:background="@color/red_500" />
<TextView
android:id="@+id/text_switch"
@ -48,6 +48,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/green" />
tools:background="@color/green_500" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -49,7 +49,7 @@
android:background="@drawable/circle_tintable"
android:visibility="gone"
app:backgroundTint="?android:windowBackground"
tools:backgroundTint="@color/red"
tools:backgroundTint="@color/red_500"
tools:visibility="visible" />
<org.thoughtcrime.securesms.components.AvatarImageView

Wyświetl plik

@ -108,7 +108,7 @@
app:layout_constraintBottom_toBottomOf="@id/story"
app:layout_constraintEnd_toEndOf="@id/story"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Signal.Story.Preview"
tools:background="@color/green"
tools:background="@color/green_500"
tools:visibility="visible" />
<com.google.android.material.imageview.ShapeableImageView

Wyświetl plik

@ -48,7 +48,7 @@
android:scaleType="centerCrop"
android:translationX="-28dp"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.WallpaperPreview"
tools:background="@color/red" />
tools:background="@color/red_500" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/preview_media_1"
@ -60,7 +60,7 @@
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.WallpaperPreview"
app:strokeColor="@color/signal_colorBackground"
app:strokeWidth="3dp"
tools:background="@color/green" />
tools:background="@color/green_500" />
</FrameLayout>

Wyświetl plik

@ -106,7 +106,7 @@
app:layout_constraintEnd_toStartOf="@id/color_bar"
app:layout_constraintStart_toStartOf="@id/color_bar"
tools:alpha="1"
tools:tint="@color/red" />
tools:tint="@color/red_500" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/color_bar"

Wyświetl plik

@ -192,7 +192,7 @@
app:layout_constraintEnd_toStartOf="@id/image_editor_hud_draw_color_bar"
app:layout_constraintStart_toStartOf="@id/image_editor_hud_draw_color_bar"
tools:alpha="1"
tools:tint="@color/red" />
tools:tint="@color/red_500" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/image_editor_hud_draw_color_bar"

Wyświetl plik

@ -24,7 +24,7 @@
app:layout_constraintEnd_toStartOf="@id/image_editor_hud_draw_color_bar"
app:layout_constraintStart_toStartOf="@id/image_editor_hud_draw_color_bar"
tools:alpha="1"
tools:tint="@color/red" />
tools:tint="@color/red_500" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/image_editor_hud_draw_color_bar"

Wyświetl plik

@ -4,5 +4,5 @@
tools:viewBindingIgnore="true"
android:layout_width="54dp"
android:layout_height="72dp"
tools:background="@color/red"
tools:background="@color/red_500"
tools:visibility="visible" />

Wyświetl plik

@ -9,7 +9,7 @@
android:background="@null"
android:clipChildren="true"
app:cardCornerRadius="8dp"
tools:background="@color/red"
tools:background="@color/red_500"
tools:visibility="visible">
<include

Wyświetl plik

@ -74,6 +74,7 @@ dependencyResolutionManagement {
alias('google-ez-vcard').to('com.googlecode.ez-vcard:ez-vcard:0.9.11')
alias('google-jsr305').to('com.google.code.findbugs:jsr305:3.0.2')
alias('google-guava-android').to('com.google.guava:guava:30.0-android')
alias('google-flexbox').to('com.google.android.flexbox:flexbox:3.0.0')
// Exoplayer
alias('exoplayer-core').to('com.google.android.exoplayer', 'exoplayer-core').versionRef('exoplayer')
@ -111,19 +112,15 @@ dependencyResolutionManagement {
alias('apache-httpclient-android').to('org.apache.httpcomponents:httpclient-android:4.3.5')
alias('glide-glide').to('com.github.bumptech.glide', 'glide').versionRef('glide')
alias('glide-compiler').to('com.github.bumptech.glide', 'compiler').versionRef('glide')
alias('photoview').to('com.github.chrisbanes:PhotoView:2.3.0')
alias('roundedimageview').to('com.makeramen:roundedimageview:2.1.0')
alias('materialish-progress').to('com.pnikosis:materialish-progress:1.5')
alias('waitingdots').to('pl.tajchert:waitingdots:0.1.0')
alias('time-duration-picker').to('mobi.upod:time-duration-picker:1.1.3')
alias('subsampling-scale-image-view').to('com.davemorrissey.labs:subsampling-scale-image-view:3.10.0')
alias('numberpickerview').to('cn.carbswang.android:NumberPickerView:1.0.9')
alias('android-tooltips').to('com.tomergoldst.android:tooltips:1.0.6')
alias('android-smsmms').to('com.klinkerapps:android-smsmms:4.0.1')
alias('stream').to('com.annimon:stream:1.1.8')
alias('colorpicker').to('com.takisoft.fix:colorpicker:0.9.1')
alias('lottie').to('com.airbnb.android:lottie:3.6.0')
alias('stickyheadergrid').to('com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4')
alias('dnsjava').to('dnsjava:dnsjava:2.1.9')
alias('nanohttpd-webserver').to('org.nanohttpd:nanohttpd-webserver:2.3.1')

Wyświetl plik

@ -20,6 +20,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
</trusted-artifacts>
</configuration>
<components>
<component group="androidx.activity" name="activity" version="1.2.4">
<artifact name="activity-1.2.4.aar">
<sha256 value="ae8e9c7de57e387d2ad90e73f3a5a5dfd502bd4f034c1dccfdb3506d1d2df81a" origin="Generated by Gradle"/>
</artifact>
<artifact name="activity-1.2.4.module">
<sha256 value="20f5634f76717910e5b4299909d6998a8078f106bdb9e15b2dd75fcfc1bd42b5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.activity" name="activity" version="1.5.1">
<artifact name="activity-1.5.1.aar">
<sha256 value="4b04b42d2c1f81c02faf0f7b6e9cc9fede10fdee8f66136cd4b99f88e8f48c0f" origin="Generated by Gradle"/>
@ -78,6 +86,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="0361d1526a4d7501255e19779e09e93cdbd07fee0e2f5c50b7a137432d510119" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.appcompat" name="appcompat" version="1.4.1">
<artifact name="appcompat-1.4.1.aar">
<sha256 value="9f1e6a958f7b02f57f9069362978baae3e73dbde9e15e9d2f119a28e0b48f799" origin="Generated by Gradle"/>
</artifact>
<artifact name="appcompat-1.4.1.module">
<sha256 value="0ec2f0dff5a05cb960a114c35619d8c9a1edbc518407f59b030ab38611fec017" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.appcompat" name="appcompat" version="1.5.1">
<artifact name="appcompat-1.5.1.aar">
<sha256 value="50f2c7756e28a7da648bd4551ee3b31e0b71863a6bf591f0ca978428219c5eab" origin="Generated by Gradle"/>
@ -86,6 +102,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="8c8e88ccafb55c142ea2ac0669fd4db8e7190a853d73fcc429a5c012ceacddf2" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.appcompat" name="appcompat-resources" version="1.4.1">
<artifact name="appcompat-resources-1.4.1.aar">
<sha256 value="c324f0a157e3cf2decd8448107de487420cb4f4fb4853cd634d71ec1f2226224" origin="Generated by Gradle"/>
</artifact>
<artifact name="appcompat-resources-1.4.1.module">
<sha256 value="e4a0fcabaa5f44a32e28de2a5623424b8898563cf0b40674e441149b50281ec5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.appcompat" name="appcompat-resources" version="1.5.1">
<artifact name="appcompat-resources-1.5.1.aar">
<sha256 value="6cfa9caae1869ffe03da9ec3ebf47ad84d70a7185ee86ec63aa8289cbb457e67" origin="Generated by Gradle"/>
@ -232,6 +256,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="e3877fa529fe29177f34a26e0790ed35544848b0c7503bfed30b2539f1686d65" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core" version="1.7.0">
<artifact name="core-1.7.0.aar">
<sha256 value="aaf6734226fff923784f92f65d78a2984dbf17534138855c5ce2038f18656e0b" origin="Generated by Gradle"/>
</artifact>
<artifact name="core-1.7.0.module">
<sha256 value="988f820899d5a4982e5c878ca1cd417970ace332ea2ff72f5be19b233fa0e788" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core" version="1.8.0">
<artifact name="core-1.8.0.aar">
<sha256 value="48c64a15ec3eb11bfb33339e5ceb70ec7f821bd2dfa2eb8675ebd5353317e792" origin="Generated by Gradle"/>
@ -319,6 +351,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="ce005162c229bf308d2d5b12fb6cad0874069cbbeaccee63a8193bd08d40de04" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.emoji2" name="emoji2" version="1.0.0">
<artifact name="emoji2-1.0.0.aar">
<sha256 value="af075731a4d17beaa29e69194a6baaf5f3512814c19aa1b8965d1920b0b7fbfa" origin="Generated by Gradle"/>
</artifact>
<artifact name="emoji2-1.0.0.module">
<sha256 value="9b16729827de022d1e0b68b8f86d146752f24c0ec0a55266672afd243088c57c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.emoji2" name="emoji2" version="1.2.0">
<artifact name="emoji2-1.2.0.aar">
<sha256 value="f31a06c150ecb03073f55a6f7b0b74a240a6a8d727c14ce76726d020570dfa8c" origin="Generated by Gradle"/>
@ -327,6 +367,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="9d1996cca03777baa1f27cd15531db983a633dae37b90f85bd53501acb56699d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.emoji2" name="emoji2-views-helper" version="1.0.0">
<artifact name="emoji2-views-helper-1.0.0.aar">
<sha256 value="39616a3d66551fc18585112ce1d12a14ac0fb588a89a6528cd97842da6221ec2" origin="Generated by Gradle"/>
</artifact>
<artifact name="emoji2-views-helper-1.0.0.module">
<sha256 value="a6a2a613c35a982f2b015fa22288dab65a7ab6940805ef8da0b7ffefb8c4c92b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.emoji2" name="emoji2-views-helper" version="1.2.0">
<artifact name="emoji2-views-helper-1.2.0.aar">
<sha256 value="7ffa4d464d9db259fca0cdb50fbd4ab63d6872bcda59468b9f7555504c7d5ac4" origin="Generated by Gradle"/>
@ -504,6 +552,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="1cedc0f971979cdec31f1542f263a8d41df6cfffc4857ea73a7d85643b73486d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-process" version="2.4.0">
<artifact name="lifecycle-process-2.4.0.aar">
<sha256 value="32f175588bd62df5672448516d9e4f3d4935d0020c0d9517958c9ffbd7c207e5" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-process-2.4.0.module">
<sha256 value="0bc4154e94dcb50f34afc8a6ea76b0ca03792ac2d800abae9dcbe905a6745a2c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-process" version="2.4.1">
<artifact name="lifecycle-process-2.4.1.aar">
<sha256 value="db649b3efa24e31052145310b002db91da346b3f89c093ec38c3046db45e794e" origin="Generated by Gradle"/>
@ -549,6 +605,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="2a7b90e5049b674b36bccfd68677b3a0b3178b3f7c2ef7ddf618d3895598c4ce" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime" version="2.4.0">
<artifact name="lifecycle-runtime-2.4.0.aar">
<sha256 value="fba2ea76a2c0a8d2804e611367d9703c5ab2ecaafebca4932b00fd672c593588" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-runtime-2.4.0.module">
<sha256 value="c089d36989257144e571fef9b5229e62ff56eab91832b1196eccb90d44b8038c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime" version="2.5.1">
<artifact name="lifecycle-runtime-2.5.1.aar">
<sha256 value="33b0d73dc2f028fceb3599bacabe563c3db6d26f3513d889595863536a4ac8c0" origin="Generated by Gradle"/>
@ -767,6 +831,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="23542c8c85cc58fafe0ae8cba201e6c9e01b4c6799223340a2d6a51d7784828c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.resourceinspection" name="resourceinspection-annotation" version="1.0.0">
<artifact name="resourceinspection-annotation-1.0.0.jar">
<sha256 value="8cff870ec6fb31db48a52f4a792335b4bf8de07e03bd37823181526433ccd5cb" origin="Generated by Gradle"/>
</artifact>
<artifact name="resourceinspection-annotation-1.0.0.module">
<sha256 value="16cf0dbbbd97081b86cf0654e9d2ef684e083a672c8ac8590345831bd24e5746" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.resourceinspection" name="resourceinspection-annotation" version="1.0.1">
<artifact name="resourceinspection-annotation-1.0.1.jar">
<sha256 value="8cff870ec6fb31db48a52f4a792335b4bf8de07e03bd37823181526433ccd5cb" origin="Generated by Gradle"/>
@ -775,6 +847,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="352a11a8d8a4c1bd6cd2c2fefff9c94ca954d7b5202a0656959db95297f6a2b7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.savedstate" name="savedstate" version="1.1.0">
<artifact name="savedstate-1.1.0.aar">
<sha256 value="d60bbe44c2c08083a17c5dc678a6d6b4d0a2d664858016ab5c049cbea90a63b7" origin="Generated by Gradle"/>
</artifact>
<artifact name="savedstate-1.1.0.module">
<sha256 value="6eea2bc150828c8fcba777e93037038e2ee3ec441c43d6acecf2c5cd9ddc5374" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.savedstate" name="savedstate" version="1.2.0">
<artifact name="savedstate-1.2.0.aar">
<sha256 value="2de528d6898e95ef020d22d9ffdf9d1f77cbdd93f92d39dfaa5d5c43b0c311c8" origin="Generated by Gradle"/>
@ -826,6 +906,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
</artifact>
</component>
<component group="androidx.startup" name="startup-runtime" version="1.0.0">
<artifact name="startup-runtime-1.0.0.aar">
<sha256 value="ff081d2db7dd28aec59f74934c514fbaf4ae5aac5258495fe10d612a3622f876" origin="Generated by Gradle"/>
</artifact>
<artifact name="startup-runtime-1.0.0.module">
<sha256 value="40effca0d6ee1fde32bc296897e54ebbcc4cf4aa29b0c531036cbd2a824a3c24" origin="Generated by Gradle"/>
</artifact>
@ -1649,6 +1732,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="313157e19cc69358139899fb582d110e361e54ab80bf681b60d7247b5b69a822" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.android.flexbox" name="flexbox" version="3.0.0">
<artifact name="flexbox-3.0.0.aar">
<sha256 value="5e19500486fd7c8e2e8c7aad6bbba9c8d0ada7057c6b32b9b5c61439814e7574" origin="Generated by Gradle"/>
</artifact>
<artifact name="flexbox-3.0.0.module">
<sha256 value="96f6e6d9f5e5db840ee3da20449e16828ed4ba2c07783dde3397d13aaf814e19" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.android.gms" name="play-services-auth" version="16.0.1">
<artifact name="play-services-auth-16.0.1.aar">
<sha256 value="aec9e1c584d442cb9f59481a50b2c66dc191872607c04d97ecb82dd0eb5149ec" origin="Generated by Gradle"/>

Wyświetl plik

@ -0,0 +1,62 @@
plugins {
id 'com.android.library'
id 'com.google.protobuf'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
buildToolsVersion BUILD_TOOL_VERSION
compileSdkVersion COMPILE_SDK
defaultConfig {
minSdkVersion MINIMUM_SDK
targetSdkVersion TARGET_SDK
multiDexEnabled true
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JAVA_VERSION
targetCompatibility JAVA_VERSION
}
kotlinOptions {
jvmTarget = '1.8'
}
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.18.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
lintChecks project(':lintchecks')
coreLibraryDesugaring libs.android.tools.desugar
api libs.androidx.annotation
implementation libs.androidx.core.ktx
implementation libs.androidx.lifecycle.common.java8
implementation libs.google.protobuf.javalite
implementation libs.androidx.sqlite
implementation libs.rxjava3.rxjava
testImplementation testLibs.junit.junit
testImplementation testLibs.mockito.core
testImplementation (testLibs.robolectric.robolectric) {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
}

Wyświetl plik

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.chrisbanes.photoview">
</manifest>

Wyświetl plik

@ -0,0 +1,39 @@
/*
Copyright 2011, 2012 Chris Banes.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.chrisbanes.photoview;
import android.annotation.TargetApi;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.view.View;
class Compat {
private static final int SIXTY_FPS_INTERVAL = 1000 / 60;
public static void postOnAnimation(View view, Runnable runnable) {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
postOnAnimationJellyBean(view, runnable);
} else {
view.postDelayed(runnable, SIXTY_FPS_INTERVAL);
}
}
@TargetApi(16)
private static void postOnAnimationJellyBean(View view, Runnable runnable) {
view.postOnAnimation(runnable);
}
}

Wyświetl plik

@ -0,0 +1,214 @@
/*
Copyright 2011, 2012 Chris Banes.
<p/>
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
<p/>
http://www.apache.org/licenses/LICENSE-2.0
<p/>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.chrisbanes.photoview;
import android.content.Context;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
/**
* Does a whole lot of gesture detecting.
*/
class CustomGestureDetector {
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private int mActivePointerIndex = 0;
private final ScaleGestureDetector mDetector;
private VelocityTracker mVelocityTracker;
private boolean mIsDragging;
private float mLastTouchX;
private float mLastTouchY;
private final float mTouchSlop;
private final float mMinimumVelocity;
private OnGestureListener mListener;
CustomGestureDetector(Context context, OnGestureListener listener) {
final ViewConfiguration configuration = ViewConfiguration
.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mTouchSlop = configuration.getScaledTouchSlop();
mListener = listener;
ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
private float lastFocusX, lastFocusY = 0;
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
return false;
if (scaleFactor >= 0) {
mListener.onScale(scaleFactor,
detector.getFocusX(),
detector.getFocusY(),
detector.getFocusX() - lastFocusX,
detector.getFocusY() - lastFocusY
);
lastFocusX = detector.getFocusX();
lastFocusY = detector.getFocusY();
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
lastFocusX = detector.getFocusX();
lastFocusY = detector.getFocusY();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// NO-OP
}
};
mDetector = new ScaleGestureDetector(context, mScaleListener);
}
private float getActiveX(MotionEvent ev) {
try {
return ev.getX(mActivePointerIndex);
} catch (Exception e) {
return ev.getX();
}
}
private float getActiveY(MotionEvent ev) {
try {
return ev.getY(mActivePointerIndex);
} catch (Exception e) {
return ev.getY();
}
}
public boolean isScaling() {
return mDetector.isInProgress();
}
public boolean isDragging() {
return mIsDragging;
}
public boolean onTouchEvent(MotionEvent ev) {
try {
mDetector.onTouchEvent(ev);
return processTouchEvent(ev);
} catch (IllegalArgumentException e) {
// Fix for support lib bug, happening when onDestroy is called
return true;
}
}
private boolean processTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
mVelocityTracker = VelocityTracker.obtain();
if (null != mVelocityTracker) {
mVelocityTracker.addMovement(ev);
}
mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev);
mIsDragging = false;
break;
case MotionEvent.ACTION_MOVE:
final float x = getActiveX(ev);
final float y = getActiveY(ev);
final float dx = x - mLastTouchX, dy = y - mLastTouchY;
if (!mIsDragging) {
// Use Pythagoras to see if drag length is larger than
// touch slop
mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
}
if (mIsDragging) {
mListener.onDrag(dx, dy);
mLastTouchX = x;
mLastTouchY = y;
if (null != mVelocityTracker) {
mVelocityTracker.addMovement(ev);
}
}
break;
case MotionEvent.ACTION_CANCEL:
mActivePointerId = INVALID_POINTER_ID;
// Recycle Velocity Tracker
if (null != mVelocityTracker) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
case MotionEvent.ACTION_UP:
mActivePointerId = INVALID_POINTER_ID;
if (mIsDragging) {
if (null != mVelocityTracker) {
mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev);
// Compute velocity within the last 1000ms
mVelocityTracker.addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1000);
final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
.getYVelocity();
// If the velocity is greater than minVelocity, call
// listener
if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
mListener.onFling(mLastTouchX, mLastTouchY, -vX,
-vY);
}
}
}
// Recycle Velocity Tracker
if (null != mVelocityTracker) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
case MotionEvent.ACTION_POINTER_UP:
final int pointerIndex = Util.getPointerIndex(ev.getAction());
final int pointerId = ev.getPointerId(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;
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
}
break;
}
mActivePointerIndex = ev
.findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
: 0);
return true;
}
}

Wyświetl plik

@ -0,0 +1,28 @@
/*
Copyright 2011, 2012 Chris Banes.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.chrisbanes.photoview;
interface OnGestureListener {
void onDrag(float dx, float dy);
void onFling(float startX, float startY, float velocityX,
float velocityY);
void onScale(float scaleFactor, float focusX, float focusY);
void onScale(float scaleFactor, float focusX, float focusY, float dx, float dy);
}

Wyświetl plik

@ -0,0 +1,18 @@
package com.github.chrisbanes.photoview;
import android.graphics.RectF;
/**
* Interface definition for a callback to be invoked when the internal Matrix has changed for
* this View.
*/
public interface OnMatrixChangedListener {
/**
* Callback for when the Matrix displaying the Drawable has changed. This could be because
* the View's bounds have changed, or the user has zoomed.
*
* @param rect - Rectangle displaying the Drawable's new bounds.
*/
void onMatrixChanged(RectF rect);
}

Wyświetl plik

@ -0,0 +1,14 @@
package com.github.chrisbanes.photoview;
import android.widget.ImageView;
/**
* Callback when the user tapped outside of the photo
*/
public interface OnOutsidePhotoTapListener {
/**
* The outside of the photo has been tapped
*/
void onOutsidePhotoTap(ImageView imageView);
}

Wyświetl plik

@ -0,0 +1,22 @@
package com.github.chrisbanes.photoview;
import android.widget.ImageView;
/**
* A callback to be invoked when the Photo is tapped with a single
* tap.
*/
public interface OnPhotoTapListener {
/**
* A callback to receive where the user taps on a photo. You will only receive a callback if
* the user taps on the actual photo, tapping on 'whitespace' will be ignored.
*
* @param view ImageView the user tapped.
* @param x where the user tapped from the of the Drawable, as percentage of the
* Drawable width.
* @param y where the user tapped from the top of the Drawable, as percentage of the
* Drawable height.
*/
void onPhotoTap(ImageView view, float x, float y);
}

Wyświetl plik

@ -0,0 +1,17 @@
package com.github.chrisbanes.photoview;
/**
* Interface definition for callback to be invoked when attached ImageView scale changes
*/
public interface OnScaleChangedListener {
/**
* Callback for when the scale changes
*
* @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in)
* @param focusX focal point X position
* @param focusY focal point Y position
*/
void onScaleChange(float scaleFactor, float focusX, float focusY);
}

Wyświetl plik

@ -0,0 +1,21 @@
package com.github.chrisbanes.photoview;
import android.view.MotionEvent;
/**
* A callback to be invoked when the ImageView is flung with a single
* touch
*/
public interface OnSingleFlingListener {
/**
* A callback to receive where the user flings on a ImageView. You will receive a callback if
* the user flings anywhere on the view.
*
* @param e1 MotionEvent the user first touch.
* @param e2 MotionEvent the user last touch.
* @param velocityX distance of user's horizontal fling.
* @param velocityY distance of user's vertical fling.
*/
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}

Wyświetl plik

@ -0,0 +1,16 @@
package com.github.chrisbanes.photoview;
/**
* Interface definition for a callback to be invoked when the photo is experiencing a drag event
*/
public interface OnViewDragListener {
/**
* Callback for when the photo is experiencing a drag event. This cannot be invoked when the
* user is scaling.
*
* @param dx The change of the coordinates in the x-direction
* @param dy The change of the coordinates in the y-direction
*/
void onDrag(float dx, float dy);
}

Wyświetl plik

@ -0,0 +1,16 @@
package com.github.chrisbanes.photoview;
import android.view.View;
public interface OnViewTapListener {
/**
* A callback to receive where the user taps on a ImageView. You will receive a callback if
* the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
*
* @param view - View the user tapped.
* @param x - where the user tapped from the left of the View.
* @param y - where the user tapped from the top of the View.
*/
void onViewTap(View view, float x, float y);
}

Wyświetl plik

@ -0,0 +1,257 @@
/*
Copyright 2011, 2012 Chris Banes.
<p>
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
<p>
http://www.apache.org/licenses/LICENSE-2.0
<p>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.chrisbanes.photoview;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.GestureDetector;
import androidx.appcompat.widget.AppCompatImageView;
/**
* A zoomable ImageView. See {@link PhotoViewAttacher} for most of the details on how the zooming
* is accomplished
*/
@SuppressWarnings("unused")
public class PhotoView extends AppCompatImageView {
private PhotoViewAttacher attacher;
private ScaleType pendingScaleType;
public PhotoView(Context context) {
this(context, null);
}
public PhotoView(Context context, AttributeSet attr) {
this(context, attr, 0);
}
public PhotoView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
init();
}
private void init() {
attacher = new PhotoViewAttacher(this);
//We always pose as a Matrix scale type, though we can change to another scale type
//via the attacher
super.setScaleType(ScaleType.MATRIX);
//apply the previously applied scale type
if (pendingScaleType != null) {
setScaleType(pendingScaleType);
pendingScaleType = null;
}
}
/**
* Get the current {@link PhotoViewAttacher} for this view. Be wary of holding on to references
* to this attacher, as it has a reference to this view, which, if a reference is held in the
* wrong place, can cause memory leaks.
*
* @return the attacher.
*/
public PhotoViewAttacher getAttacher() {
return attacher;
}
@Override
public ScaleType getScaleType() {
return attacher.getScaleType();
}
@Override
public Matrix getImageMatrix() {
return attacher.getImageMatrix();
}
@Override
public void setOnLongClickListener(OnLongClickListener l) {
attacher.setOnLongClickListener(l);
}
@Override
public void setOnClickListener(OnClickListener l) {
attacher.setOnClickListener(l);
}
@Override
public void setScaleType(ScaleType scaleType) {
if (attacher == null) {
pendingScaleType = scaleType;
} else {
attacher.setScaleType(scaleType);
}
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
// setImageBitmap calls through to this method
if (attacher != null) {
attacher.update();
}
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
if (attacher != null) {
attacher.update();
}
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
if (attacher != null) {
attacher.update();
}
}
@Override
protected boolean setFrame(int l, int t, int r, int b) {
boolean changed = super.setFrame(l, t, r, b);
if (changed) {
attacher.update();
}
return changed;
}
public void setRotationTo(float rotationDegree) {
attacher.setRotationTo(rotationDegree);
}
public void setRotationBy(float rotationDegree) {
attacher.setRotationBy(rotationDegree);
}
public boolean isZoomable() {
return attacher.isZoomable();
}
public void setZoomable(boolean zoomable) {
attacher.setZoomable(zoomable);
}
public RectF getDisplayRect() {
return attacher.getDisplayRect();
}
public void getDisplayMatrix(Matrix matrix) {
attacher.getDisplayMatrix(matrix);
}
@SuppressWarnings("UnusedReturnValue") public boolean setDisplayMatrix(Matrix finalRectangle) {
return attacher.setDisplayMatrix(finalRectangle);
}
public void getSuppMatrix(Matrix matrix) {
attacher.getSuppMatrix(matrix);
}
public boolean setSuppMatrix(Matrix matrix) {
return attacher.setDisplayMatrix(matrix);
}
public float getMinimumScale() {
return attacher.getMinimumScale();
}
public float getMediumScale() {
return attacher.getMediumScale();
}
public float getMaximumScale() {
return attacher.getMaximumScale();
}
public float getScale() {
return attacher.getScale();
}
public void setAllowParentInterceptOnEdge(boolean allow) {
attacher.setAllowParentInterceptOnEdge(allow);
}
public void setMinimumScale(float minimumScale) {
attacher.setMinimumScale(minimumScale);
}
public void setMediumScale(float mediumScale) {
attacher.setMediumScale(mediumScale);
}
public void setMaximumScale(float maximumScale) {
attacher.setMaximumScale(maximumScale);
}
public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
attacher.setScaleLevels(minimumScale, mediumScale, maximumScale);
}
public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
attacher.setOnMatrixChangeListener(listener);
}
public void setOnPhotoTapListener(OnPhotoTapListener listener) {
attacher.setOnPhotoTapListener(listener);
}
public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener listener) {
attacher.setOnOutsidePhotoTapListener(listener);
}
public void setOnViewTapListener(OnViewTapListener listener) {
attacher.setOnViewTapListener(listener);
}
public void setOnViewDragListener(OnViewDragListener listener) {
attacher.setOnViewDragListener(listener);
}
public void setScale(float scale) {
attacher.setScale(scale);
}
public void setScale(float scale, boolean animate) {
attacher.setScale(scale, animate);
}
public void setScale(float scale, float focalX, float focalY, boolean animate) {
attacher.setScale(scale, focalX, focalY, animate);
}
public void setZoomTransitionDuration(int milliseconds) {
attacher.setZoomTransitionDuration(milliseconds);
}
public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener onDoubleTapListener) {
attacher.setOnDoubleTapListener(onDoubleTapListener);
}
public void setOnScaleChangeListener(OnScaleChangedListener onScaleChangedListener) {
attacher.setOnScaleChangeListener(onScaleChangedListener);
}
public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {
attacher.setOnSingleFlingListener(onSingleFlingListener);
}
}

Wyświetl plik

@ -0,0 +1,823 @@
/*
Copyright 2011, 2012 Chris Banes.
<p>
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
<p>
http://www.apache.org/licenses/LICENSE-2.0
<p>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.chrisbanes.photoview;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewParent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.OverScroller;
/**
* The component of {@link PhotoView} which does the work allowing for zooming, scaling, panning, etc.
* It is made public in case you need to subclass something other than AppCompatImageView and still
* gain the functionality that {@link PhotoView} offers
*/
public class PhotoViewAttacher implements View.OnTouchListener,
View.OnLayoutChangeListener {
private static float DEFAULT_MAX_SCALE = 3.0f;
private static float DEFAULT_MID_SCALE = 1.75f;
private static float DEFAULT_MIN_SCALE = 1.0f;
private static int DEFAULT_ZOOM_DURATION = 200;
private static final int HORIZONTAL_EDGE_NONE = -1;
private static final int HORIZONTAL_EDGE_LEFT = 0;
private static final int HORIZONTAL_EDGE_RIGHT = 1;
private static final int HORIZONTAL_EDGE_BOTH = 2;
private static final int VERTICAL_EDGE_NONE = -1;
private static final int VERTICAL_EDGE_TOP = 0;
private static final int VERTICAL_EDGE_BOTTOM = 1;
private static final int VERTICAL_EDGE_BOTH = 2;
private static int SINGLE_TOUCH = 1;
private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
private int mZoomDuration = DEFAULT_ZOOM_DURATION;
private float mMinScale = DEFAULT_MIN_SCALE;
private float mMidScale = DEFAULT_MID_SCALE;
private float mMaxScale = DEFAULT_MAX_SCALE;
private boolean mAllowParentInterceptOnEdge = true;
private boolean mBlockParentIntercept = false;
private ImageView mImageView;
// Gesture Detectors
private GestureDetector mGestureDetector;
private CustomGestureDetector mScaleDragDetector;
// These are set so we don't keep allocating them on the heap
private final Matrix mBaseMatrix = new Matrix();
private final Matrix mDrawMatrix = new Matrix();
private final Matrix mSuppMatrix = new Matrix();
private final RectF mDisplayRect = new RectF();
private final float[] mMatrixValues = new float[9];
// Listeners
private OnMatrixChangedListener mMatrixChangeListener;
private OnPhotoTapListener mPhotoTapListener;
private OnOutsidePhotoTapListener mOutsidePhotoTapListener;
private OnViewTapListener mViewTapListener;
private View.OnClickListener mOnClickListener;
private OnLongClickListener mLongClickListener;
private OnScaleChangedListener mScaleChangeListener;
private OnSingleFlingListener mSingleFlingListener;
private OnViewDragListener mOnViewDragListener;
private FlingRunnable mCurrentFlingRunnable;
private int mHorizontalScrollEdge = HORIZONTAL_EDGE_BOTH;
private int mVerticalScrollEdge = VERTICAL_EDGE_BOTH;
private float mBaseRotation;
private boolean mZoomEnabled = true;
private ScaleType mScaleType = ScaleType.FIT_CENTER;
private OnGestureListener onGestureListener = new OnGestureListener() {
@Override
public void onDrag(float dx, float dy) {
if (mScaleDragDetector.isScaling()) {
return; // Do not drag if we are already scaling
}
if (mOnViewDragListener != null) {
mOnViewDragListener.onDrag(dx, dy);
}
mSuppMatrix.postTranslate(dx, dy);
checkAndDisplayMatrix();
/*
* Here we decide whether to let the ImageView's parent to start taking
* over the touch event.
*
* First we check whether this function is enabled. We never want the
* parent to take over if we're scaling. We then check the edge we're
* on, and the direction of the scroll (i.e. if we're pulling against
* the edge, aka 'overscrolling', let the parent take over).
*/
ViewParent parent = mImageView.getParent();
if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) {
if (mHorizontalScrollEdge == HORIZONTAL_EDGE_BOTH
|| (mHorizontalScrollEdge == HORIZONTAL_EDGE_LEFT && dx >= 1f)
|| (mHorizontalScrollEdge == HORIZONTAL_EDGE_RIGHT && dx <= -1f)
|| (mVerticalScrollEdge == VERTICAL_EDGE_TOP && dy >= 1f)
|| (mVerticalScrollEdge == VERTICAL_EDGE_BOTTOM && dy <= -1f)) {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
}
}
} else {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
@Override
public void onFling(float startX, float startY, float velocityX, float velocityY) {
mCurrentFlingRunnable = new FlingRunnable(mImageView.getContext());
mCurrentFlingRunnable.fling(getImageViewWidth(mImageView),
getImageViewHeight(mImageView), (int) velocityX, (int) velocityY);
mImageView.post(mCurrentFlingRunnable);
}
@Override
public void onScale(float scaleFactor, float focusX, float focusY) {
onScale(scaleFactor, focusX, focusY, 0, 0);
}
@Override
public void onScale(float scaleFactor, float focusX, float focusY, float dx, float dy) {
if (getScale() < mMaxScale || scaleFactor < 1f) {
if (mScaleChangeListener != null) {
mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY);
}
mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
mSuppMatrix.postTranslate(dx, dy);
checkAndDisplayMatrix();
}
}
};
public PhotoViewAttacher(ImageView imageView) {
mImageView = imageView;
imageView.setOnTouchListener(this);
imageView.addOnLayoutChangeListener(this);
if (imageView.isInEditMode()) {
return;
}
mBaseRotation = 0.0f;
// Create Gesture Detectors...
mScaleDragDetector = new CustomGestureDetector(imageView.getContext(), onGestureListener);
mGestureDetector = new GestureDetector(imageView.getContext(), new GestureDetector.SimpleOnGestureListener() {
// forward long click listener
@Override
public void onLongPress(MotionEvent e) {
if (mLongClickListener != null) {
mLongClickListener.onLongClick(mImageView);
}
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
if (mSingleFlingListener != null) {
if (getScale() > DEFAULT_MIN_SCALE) {
return false;
}
if (e1.getPointerCount() > SINGLE_TOUCH
|| e2.getPointerCount() > SINGLE_TOUCH) {
return false;
}
return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY);
}
return false;
}
});
mGestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mOnClickListener != null) {
mOnClickListener.onClick(mImageView);
}
final RectF displayRect = getDisplayRect();
final float x = e.getX(), y = e.getY();
if (mViewTapListener != null) {
mViewTapListener.onViewTap(mImageView, x, y);
}
if (displayRect != null) {
// Check to see if the user tapped on the photo
if (displayRect.contains(x, y)) {
float xResult = (x - displayRect.left)
/ displayRect.width();
float yResult = (y - displayRect.top)
/ displayRect.height();
if (mPhotoTapListener != null) {
mPhotoTapListener.onPhotoTap(mImageView, xResult, yResult);
}
return true;
} else {
if (mOutsidePhotoTapListener != null) {
mOutsidePhotoTapListener.onOutsidePhotoTap(mImageView);
}
}
}
return false;
}
@Override
public boolean onDoubleTap(MotionEvent ev) {
try {
float scale = getScale();
float x = ev.getX();
float y = ev.getY();
if (scale < getMediumScale()) {
setScale(getMediumScale(), x, y, true);
} else if (scale >= getMediumScale() && scale < getMaximumScale()) {
setScale(getMaximumScale(), x, y, true);
} else {
setScale(getMinimumScale(), x, y, true);
}
} catch (ArrayIndexOutOfBoundsException e) {
// Can sometimes happen when getX() and getY() is called
}
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
// Wait for the confirmed onDoubleTap() instead
return false;
}
});
}
public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);
}
public void setOnScaleChangeListener(OnScaleChangedListener onScaleChangeListener) {
this.mScaleChangeListener = onScaleChangeListener;
}
public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {
this.mSingleFlingListener = onSingleFlingListener;
}
@Deprecated
public boolean isZoomEnabled() {
return mZoomEnabled;
}
public RectF getDisplayRect() {
checkMatrixBounds();
return getDisplayRect(getDrawMatrix());
}
public boolean setDisplayMatrix(Matrix finalMatrix) {
if (finalMatrix == null) {
throw new IllegalArgumentException("Matrix cannot be null");
}
if (mImageView.getDrawable() == null) {
return false;
}
mSuppMatrix.set(finalMatrix);
checkAndDisplayMatrix();
return true;
}
public void setBaseRotation(final float degrees) {
mBaseRotation = degrees % 360;
update();
setRotationBy(mBaseRotation);
checkAndDisplayMatrix();
}
public void setRotationTo(float degrees) {
mSuppMatrix.setRotate(degrees % 360);
checkAndDisplayMatrix();
}
public void setRotationBy(float degrees) {
mSuppMatrix.postRotate(degrees % 360);
checkAndDisplayMatrix();
}
public float getMinimumScale() {
return mMinScale;
}
public float getMediumScale() {
return mMidScale;
}
public float getMaximumScale() {
return mMaxScale;
}
public float getScale() {
return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow
(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));
}
public ScaleType getScaleType() {
return mScaleType;
}
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int
oldRight, int oldBottom) {
// Update our base matrix, as the bounds have changed
if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
updateBaseMatrix(mImageView.getDrawable());
}
}
@Override
public boolean onTouch(View v, MotionEvent ev) {
boolean handled = false;
if (mZoomEnabled && Util.hasDrawable((ImageView) v)) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
ViewParent parent = v.getParent();
// First, disable the Parent from intercepting the touch
// event
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
// If we're flinging, and the user presses down, cancel
// fling
cancelFling();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// If the user has zoomed less than min scale, zoom back
// to min scale
if (getScale() < mMinScale) {
RectF rect = getDisplayRect();
if (rect != null) {
v.post(new AnimatedZoomRunnable(getScale(), mMinScale,
rect.centerX(), rect.centerY()));
handled = true;
}
} else if (getScale() > mMaxScale) {
RectF rect = getDisplayRect();
if (rect != null) {
v.post(new AnimatedZoomRunnable(getScale(), mMaxScale,
rect.centerX(), rect.centerY()));
handled = true;
}
}
break;
}
// Try the Scale/Drag detector
if (mScaleDragDetector != null) {
boolean wasScaling = mScaleDragDetector.isScaling();
boolean wasDragging = mScaleDragDetector.isDragging();
handled = mScaleDragDetector.onTouchEvent(ev);
boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling();
boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging();
mBlockParentIntercept = didntScale && didntDrag;
}
// Check to see if the user double tapped
if (mGestureDetector != null && mGestureDetector.onTouchEvent(ev)) {
handled = true;
}
}
return handled;
}
public void setAllowParentInterceptOnEdge(boolean allow) {
mAllowParentInterceptOnEdge = allow;
}
public void setMinimumScale(float minimumScale) {
Util.checkZoomLevels(minimumScale, mMidScale, mMaxScale);
mMinScale = minimumScale;
}
public void setMediumScale(float mediumScale) {
Util.checkZoomLevels(mMinScale, mediumScale, mMaxScale);
mMidScale = mediumScale;
}
public void setMaximumScale(float maximumScale) {
Util.checkZoomLevels(mMinScale, mMidScale, maximumScale);
mMaxScale = maximumScale;
}
public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
Util.checkZoomLevels(minimumScale, mediumScale, maximumScale);
mMinScale = minimumScale;
mMidScale = mediumScale;
mMaxScale = maximumScale;
}
public void setOnLongClickListener(OnLongClickListener listener) {
mLongClickListener = listener;
}
public void setOnClickListener(View.OnClickListener listener) {
mOnClickListener = listener;
}
public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
mMatrixChangeListener = listener;
}
public void setOnPhotoTapListener(OnPhotoTapListener listener) {
mPhotoTapListener = listener;
}
public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener mOutsidePhotoTapListener) {
this.mOutsidePhotoTapListener = mOutsidePhotoTapListener;
}
public void setOnViewTapListener(OnViewTapListener listener) {
mViewTapListener = listener;
}
public void setOnViewDragListener(OnViewDragListener listener) {
mOnViewDragListener = listener;
}
public void setScale(float scale) {
setScale(scale, false);
}
public void setScale(float scale, boolean animate) {
setScale(scale,
(mImageView.getRight()) / 2,
(mImageView.getBottom()) / 2,
animate);
}
public void setScale(float scale, float focalX, float focalY,
boolean animate) {
// Check to see if the scale is within bounds
if (scale < mMinScale || scale > mMaxScale) {
throw new IllegalArgumentException("Scale must be within the range of minScale and maxScale");
}
if (animate) {
mImageView.post(new AnimatedZoomRunnable(getScale(), scale,
focalX, focalY));
} else {
mSuppMatrix.setScale(scale, scale, focalX, focalY);
checkAndDisplayMatrix();
}
}
/**
* Set the zoom interpolator
*
* @param interpolator the zoom interpolator
*/
public void setZoomInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
}
public void setScaleType(ScaleType scaleType) {
if (Util.isSupportedScaleType(scaleType) && scaleType != mScaleType) {
mScaleType = scaleType;
update();
}
}
public boolean isZoomable() {
return mZoomEnabled;
}
public void setZoomable(boolean zoomable) {
mZoomEnabled = zoomable;
update();
}
public void update() {
if (mZoomEnabled) {
// Update the base matrix using the current drawable
updateBaseMatrix(mImageView.getDrawable());
} else {
// Reset the Matrix...
resetMatrix();
}
}
/**
* Get the display matrix
*
* @param matrix target matrix to copy to
*/
public void getDisplayMatrix(Matrix matrix) {
matrix.set(getDrawMatrix());
}
/**
* Get the current support matrix
*/
public void getSuppMatrix(Matrix matrix) {
matrix.set(mSuppMatrix);
}
private Matrix getDrawMatrix() {
mDrawMatrix.set(mBaseMatrix);
mDrawMatrix.postConcat(mSuppMatrix);
return mDrawMatrix;
}
public Matrix getImageMatrix() {
return mDrawMatrix;
}
public void setZoomTransitionDuration(int milliseconds) {
this.mZoomDuration = milliseconds;
}
/**
* Helper method that 'unpacks' a Matrix and returns the required value
*
* @param matrix Matrix to unpack
* @param whichValue Which value from Matrix.M* to return
* @return returned value
*/
private float getValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
/**
* Resets the Matrix back to FIT_CENTER, and then displays its contents
*/
private void resetMatrix() {
mSuppMatrix.reset();
setRotationBy(mBaseRotation);
setImageViewMatrix(getDrawMatrix());
checkMatrixBounds();
}
private void setImageViewMatrix(Matrix matrix) {
mImageView.setImageMatrix(matrix);
// Call MatrixChangedListener if needed
if (mMatrixChangeListener != null) {
RectF displayRect = getDisplayRect(matrix);
if (displayRect != null) {
mMatrixChangeListener.onMatrixChanged(displayRect);
}
}
}
/**
* Helper method that simply checks the Matrix, and then displays the result
*/
private void checkAndDisplayMatrix() {
if (checkMatrixBounds()) {
setImageViewMatrix(getDrawMatrix());
}
}
/**
* Helper method that maps the supplied Matrix to the current Drawable
*
* @param matrix - Matrix to map Drawable against
* @return RectF - Displayed Rectangle
*/
private RectF getDisplayRect(Matrix matrix) {
Drawable d = mImageView.getDrawable();
if (d != null) {
mDisplayRect.set(0, 0, d.getIntrinsicWidth(),
d.getIntrinsicHeight());
matrix.mapRect(mDisplayRect);
return mDisplayRect;
}
return null;
}
/**
* Calculate Matrix for FIT_CENTER
*
* @param drawable - Drawable being displayed
*/
private void updateBaseMatrix(Drawable drawable) {
if (drawable == null) {
return;
}
final float viewWidth = getImageViewWidth(mImageView);
final float viewHeight = getImageViewHeight(mImageView);
final int drawableWidth = drawable.getIntrinsicWidth();
final int drawableHeight = drawable.getIntrinsicHeight();
mBaseMatrix.reset();
final float widthScale = viewWidth / drawableWidth;
final float heightScale = viewHeight / drawableHeight;
if (mScaleType == ScaleType.CENTER) {
mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,
(viewHeight - drawableHeight) / 2F);
} else if (mScaleType == ScaleType.CENTER_CROP) {
float scale = Math.max(widthScale, heightScale);
mBaseMatrix.postScale(scale, scale);
mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
(viewHeight - drawableHeight * scale) / 2F);
} else if (mScaleType == ScaleType.CENTER_INSIDE) {
float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
mBaseMatrix.postScale(scale, scale);
mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
(viewHeight - drawableHeight * scale) / 2F);
} else {
RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
if ((int) mBaseRotation % 180 != 0) {
mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);
}
switch (mScaleType) {
case FIT_CENTER:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
break;
case FIT_START:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
break;
case FIT_END:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
break;
case FIT_XY:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
break;
default:
break;
}
}
resetMatrix();
}
private boolean checkMatrixBounds() {
final RectF rect = getDisplayRect(getDrawMatrix());
if (rect == null) {
return false;
}
final float height = rect.height(), width = rect.width();
float deltaX = 0, deltaY = 0;
final int viewHeight = getImageViewHeight(mImageView);
if (height <= viewHeight) {
switch (mScaleType) {
case FIT_START:
deltaY = -rect.top;
break;
case FIT_END:
deltaY = viewHeight - height - rect.top;
break;
default:
deltaY = (viewHeight - height) / 2 - rect.top;
break;
}
mVerticalScrollEdge = VERTICAL_EDGE_BOTH;
} else if (rect.top > 0) {
mVerticalScrollEdge = VERTICAL_EDGE_TOP;
deltaY = -rect.top;
} else if (rect.bottom < viewHeight) {
mVerticalScrollEdge = VERTICAL_EDGE_BOTTOM;
deltaY = viewHeight - rect.bottom;
} else {
mVerticalScrollEdge = VERTICAL_EDGE_NONE;
}
final int viewWidth = getImageViewWidth(mImageView);
if (width <= viewWidth) {
switch (mScaleType) {
case FIT_START:
deltaX = -rect.left;
break;
case FIT_END:
deltaX = viewWidth - width - rect.left;
break;
default:
deltaX = (viewWidth - width) / 2 - rect.left;
break;
}
mHorizontalScrollEdge = HORIZONTAL_EDGE_BOTH;
} else if (rect.left > 0) {
mHorizontalScrollEdge = HORIZONTAL_EDGE_LEFT;
deltaX = -rect.left;
} else if (rect.right < viewWidth) {
deltaX = viewWidth - rect.right;
mHorizontalScrollEdge = HORIZONTAL_EDGE_RIGHT;
} else {
mHorizontalScrollEdge = HORIZONTAL_EDGE_NONE;
}
// Finally actually translate the matrix
mSuppMatrix.postTranslate(deltaX, deltaY);
return true;
}
private int getImageViewWidth(ImageView imageView) {
return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
}
private int getImageViewHeight(ImageView imageView) {
return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
}
private void cancelFling() {
if (mCurrentFlingRunnable != null) {
mCurrentFlingRunnable.cancelFling();
mCurrentFlingRunnable = null;
}
}
private class AnimatedZoomRunnable implements Runnable {
private final float mFocalX, mFocalY;
private final long mStartTime;
private final float mZoomStart, mZoomEnd;
public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,
final float focalX, final float focalY) {
mFocalX = focalX;
mFocalY = focalY;
mStartTime = System.currentTimeMillis();
mZoomStart = currentZoom;
mZoomEnd = targetZoom;
}
@Override
public void run() {
float t = interpolate();
float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
float deltaScale = scale / getScale();
onGestureListener.onScale(deltaScale, mFocalX, mFocalY);
// We haven't hit our target scale yet, so post ourselves again
if (t < 1f) {
Compat.postOnAnimation(mImageView, this);
}
}
private float interpolate() {
float t = 1f * (System.currentTimeMillis() - mStartTime) / mZoomDuration;
t = Math.min(1f, t);
t = mInterpolator.getInterpolation(t);
return t;
}
}
private class FlingRunnable implements Runnable {
private final OverScroller mScroller;
private int mCurrentX, mCurrentY;
public FlingRunnable(Context context) {
mScroller = new OverScroller(context);
}
public void cancelFling() {
mScroller.forceFinished(true);
}
public void fling(int viewWidth, int viewHeight, int velocityX,
int velocityY) {
final RectF rect = getDisplayRect();
if (rect == null) {
return;
}
final int startX = Math.round(-rect.left);
final int minX, maxX, minY, maxY;
if (viewWidth < rect.width()) {
minX = 0;
maxX = Math.round(rect.width() - viewWidth);
} else {
minX = maxX = startX;
}
final int startY = Math.round(-rect.top);
if (viewHeight < rect.height()) {
minY = 0;
maxY = Math.round(rect.height() - viewHeight);
} else {
minY = maxY = startY;
}
mCurrentX = startX;
mCurrentY = startY;
// If we actually can move, fling the scroller
if (startX != maxX || startY != maxY) {
mScroller.fling(startX, startY, velocityX, velocityY, minX,
maxX, minY, maxY, 0, 0);
}
}
@Override
public void run() {
if (mScroller.isFinished()) {
return; // remaining post that should not be handled
}
if (mScroller.computeScrollOffset()) {
final int newX = mScroller.getCurrX();
final int newY = mScroller.getCurrY();
mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
checkAndDisplayMatrix();
mCurrentX = newX;
mCurrentY = newY;
// Post On animation
Compat.postOnAnimation(mImageView, this);
}
}
}
}

Wyświetl plik

@ -0,0 +1,37 @@
package com.github.chrisbanes.photoview;
import android.view.MotionEvent;
import android.widget.ImageView;
class Util {
static void checkZoomLevels(float minZoom, float midZoom,
float maxZoom) {
if (minZoom >= midZoom) {
throw new IllegalArgumentException(
"Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value");
} else if (midZoom >= maxZoom) {
throw new IllegalArgumentException(
"Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value");
}
}
static boolean hasDrawable(ImageView imageView) {
return imageView.getDrawable() != null;
}
static boolean isSupportedScaleType(final ImageView.ScaleType scaleType) {
if (scaleType == null) {
return false;
}
switch (scaleType) {
case MATRIX:
throw new IllegalStateException("Matrix scale type is not supported");
}
return true;
}
static int getPointerIndex(int action) {
return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
}

Wyświetl plik

@ -22,6 +22,8 @@ include ':contacts'
include ':contacts-app'
include ':qr'
include ':qr-app'
include ':sticky-header-grid'
include ':photoview'
project(':app').name = 'Signal-Android'
project(':paging').projectDir = file('paging/lib')

Wyświetl plik

@ -0,0 +1,2 @@
This was migrated from [Codewaves/Sticky-Header-Grid](https://github.com/Codewaves/Sticky-Header-Grid), because the artifact is still on published on jcenter.
The project was small enough that it was easier to just bring the two files in as a source dependency.

Wyświetl plik

@ -0,0 +1,62 @@
plugins {
id 'com.android.library'
id 'com.google.protobuf'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
buildToolsVersion BUILD_TOOL_VERSION
compileSdkVersion COMPILE_SDK
defaultConfig {
minSdkVersion MINIMUM_SDK
targetSdkVersion TARGET_SDK
multiDexEnabled true
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JAVA_VERSION
targetCompatibility JAVA_VERSION
}
kotlinOptions {
jvmTarget = '1.8'
}
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.18.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
dependencies {
implementation 'androidx.recyclerview:recyclerview:1.2.1'
lintChecks project(':lintchecks')
coreLibraryDesugaring libs.android.tools.desugar
api libs.androidx.annotation
implementation libs.androidx.core.ktx
implementation libs.androidx.lifecycle.common.java8
implementation libs.google.protobuf.javalite
implementation libs.androidx.sqlite
implementation libs.rxjava3.rxjava
testImplementation testLibs.junit.junit
testImplementation testLibs.mockito.core
testImplementation (testLibs.robolectric.robolectric) {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
}

Wyświetl plik

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codewaves.stickyheadergrid">
</manifest>

Wyświetl plik

@ -0,0 +1,613 @@
package com.codewaves.stickyheadergrid;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
/**
* Created by Sergej Kravcenko on 4/24/2017.
* Copyright (c) 2017 Sergej Kravcenko
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public abstract class StickyHeaderGridAdapter extends RecyclerView.Adapter<StickyHeaderGridAdapter.ViewHolder> {
public static final String TAG = "StickyHeaderGridAdapter";
public static final int TYPE_HEADER = 0;
public static final int TYPE_ITEM = 1;
private ArrayList<Section> mSections;
private int[] mSectionIndices;
private int mTotalItemNumber;
@SuppressWarnings("WeakerAccess")
public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
public boolean isHeader() {
return false;
}
public int getSectionItemViewType() {
return StickyHeaderGridAdapter.externalViewType(getItemViewType());
}
}
public static class ItemViewHolder extends ViewHolder {
public ItemViewHolder(View itemView) {
super(itemView);
}
}
public static class HeaderViewHolder extends ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
}
@Override
public boolean isHeader() {
return true;
}
}
private static class Section {
private int position;
private int itemNumber;
private int length;
}
private void calculateSections() {
mSections = new ArrayList<>();
int total = 0;
int sectionCount = getSectionCount();
for (int s = 0; s < sectionCount; s++) {
final Section section = new Section();
section.position = total;
section.itemNumber = getSectionItemCount(s);
section.length = section.itemNumber + 1;
mSections.add(section);
total += section.length;
}
mTotalItemNumber = total;
total = 0;
mSectionIndices = new int[mTotalItemNumber];
for (int s = 0; s < sectionCount; s++) {
final Section section = mSections.get(s);
for (int i = 0; i < section.length; i++) {
mSectionIndices[total + i] = s;
}
total += section.length;
}
}
protected int getItemViewInternalType(int position) {
final int section = getAdapterPositionSection(position);
final Section sectionObject = mSections.get(section);
final int sectionPosition = position - sectionObject.position;
return getItemViewInternalType(section, sectionPosition);
}
private int getItemViewInternalType(int section, int position) {
return position == 0 ? TYPE_HEADER : TYPE_ITEM;
}
static private int internalViewType(int type) {
return type & 0xFF;
}
static private int externalViewType(int type) {
return type >> 8;
}
@Override
final public int getItemCount() {
if (mSections == null) {
calculateSections();
}
return mTotalItemNumber;
}
@NonNull
@Override
final public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final int internalType = internalViewType(viewType);
final int externalType = externalViewType(viewType);
switch (internalType) {
case TYPE_HEADER:
return onCreateHeaderViewHolder(parent, externalType);
case TYPE_ITEM:
return onCreateItemViewHolder(parent, externalType);
default:
throw new InvalidParameterException("Invalid viewType: " + viewType);
}
}
@Override
final public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
if (mSections == null) {
calculateSections();
}
final int section = mSectionIndices[position];
final int internalType = internalViewType(holder.getItemViewType());
final int externalType = externalViewType(holder.getItemViewType());
switch (internalType) {
case TYPE_HEADER:
onBindHeaderViewHolder((HeaderViewHolder)holder, section);
break;
case TYPE_ITEM:
final ItemViewHolder itemHolder = (ItemViewHolder)holder;
final int offset = getItemSectionOffset(section, position);
onBindItemViewHolder((ItemViewHolder)holder, section, offset);
break;
default:
throw new InvalidParameterException("invalid viewType: " + internalType);
}
}
@Override
final public int getItemViewType(int position) {
final int section = getAdapterPositionSection(position);
final Section sectionObject = mSections.get(section);
final int sectionPosition = position - sectionObject.position;
final int internalType = getItemViewInternalType(section, sectionPosition);
int externalType = 0;
switch (internalType) {
case TYPE_HEADER:
externalType = getSectionHeaderViewType(section);
break;
case TYPE_ITEM:
externalType = getSectionItemViewType(section, sectionPosition - 1);
break;
}
return ((externalType & 0xFF) << 8) | (internalType & 0xFF);
}
// Helpers
private int getItemSectionHeaderPosition(int position) {
return getSectionHeaderPosition(getAdapterPositionSection(position));
}
private int getAdapterPosition(int section, int offset) {
if (mSections == null) {
calculateSections();
}
if (section < 0) {
throw new IndexOutOfBoundsException("section " + section + " < 0");
}
if (section >= mSections.size()) {
throw new IndexOutOfBoundsException("section " + section + " >=" + mSections.size());
}
final Section sectionObject = mSections.get(section);
return sectionObject.position + offset;
}
/**
* Given a <code>section</code> and an adapter <code>position</code> get the offset of an item
* inside <code>section</code>.
*
* @param section section to query
* @param position adapter position
* @return The item offset inside the section.
*/
public int getItemSectionOffset(int section, int position) {
if (mSections == null) {
calculateSections();
}
if (section < 0) {
throw new IndexOutOfBoundsException("section " + section + " < 0");
}
if (section >= mSections.size()) {
throw new IndexOutOfBoundsException("section " + section + " >=" + mSections.size());
}
final Section sectionObject = mSections.get(section);
final int localPosition = position - sectionObject.position;
if (localPosition >= sectionObject.length) {
throw new IndexOutOfBoundsException("localPosition: " + localPosition + " >=" + sectionObject.length);
}
return localPosition - 1;
}
/**
* Returns the section index having item or header with provided
* provider <code>position</code>.
*
* @param position adapter position
* @return The section containing provided adapter position.
*/
public int getAdapterPositionSection(int position) {
if (mSections == null) {
calculateSections();
}
if (getItemCount() == 0) {
return NO_POSITION;
}
if (position < 0) {
throw new IndexOutOfBoundsException("position " + position + " < 0");
}
if (position >= getItemCount()) {
throw new IndexOutOfBoundsException("position " + position + " >=" + getItemCount());
}
return mSectionIndices[position];
}
/**
* Returns the adapter position for given <code>section</code> header. Use
* this only for {@link RecyclerView#scrollToPosition(int)} or similar functions.
* Never directly manipulate adapter items using this position.
*
* @param section section to query
* @return The adapter position.
*/
public int getSectionHeaderPosition(int section) {
return getAdapterPosition(section, 0);
}
/**
* Returns the adapter position for given <code>section</code> and
* <code>offset</code>. Use this only for {@link RecyclerView#scrollToPosition(int)}
* or similar functions. Never directly manipulate adapter items using this position.
*
* @param section section to query
* @param position item position inside the <code>section</code>
* @return The adapter position.
*/
public int getSectionItemPosition(int section, int position) {
return getAdapterPosition(section, position + 1);
}
// Overrides
/**
* Returns the total number of sections in the data set held by the adapter.
*
* @return The total number of section in this adapter.
*/
public int getSectionCount() {
return 0;
}
/**
* Returns the number of items in the <code>section</code>.
*
* @param section section to query
* @return The total number of items in the <code>section</code>.
*/
public int getSectionItemCount(int section) {
return 0;
}
/**
* Return the view type of the <code>section</code> header for the purposes
* of view recycling.
*
* <p>The default implementation of this method returns 0, making the assumption of
* a single view type for the headers. Unlike ListView adapters, types need not
* be contiguous. Consider using id resources to uniquely identify item view types.
*
* @param section section to query
* @return integer value identifying the type of the view needed to represent the header in
* <code>section</code>. Type codes need not be contiguous.
*/
public int getSectionHeaderViewType(int section) {
return 0;
}
/**
* Return the view type of the item at <code>position</code> in <code>section</code> for
* the purposes of view recycling.
*
* <p>The default implementation of this method returns 0, making the assumption of
* a single view type for the adapter. Unlike ListView adapters, types need not
* be contiguous. Consider using id resources to uniquely identify item view types.
*
* @param section section to query
* @param offset section position to query
* @return integer value identifying the type of the view needed to represent the item at
* <code>position</code> in <code>section</code>. Type codes need not be
* contiguous.
*/
public int getSectionItemViewType(int section, int offset) {
return 0;
}
/**
* Returns true if header in <code>section</code> is sticky.
*
* @param section section to query
* @return true if <code>section</code> header is sticky.
*/
public boolean isSectionHeaderSticky(int section) {
return true;
}
/**
* Called when RecyclerView needs a new {@link HeaderViewHolder} of the given type to represent
* a header.
* <p>
* This new HeaderViewHolder should be constructed with a new View that can represent the headers
* of the given type. You can either create a new View manually or inflate it from an XML
* layout file.
* <p>
* The new HeaderViewHolder will be used to display items of the adapter using
* {@link #onBindHeaderViewHolder(HeaderViewHolder, int)}. Since it will be re-used to display
* different items in the data set, it is a good idea to cache references to sub views of
* the View to avoid unnecessary {@link View#findViewById(int)} calls.
*
* @param parent The ViewGroup into which the new View will be added after it is bound to
* an adapter position.
* @param headerType The view type of the new View.
*
* @return A new ViewHolder that holds a View of the given view type.
* @see #getSectionHeaderViewType(int)
* @see #onBindHeaderViewHolder(HeaderViewHolder, int)
*/
public abstract HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int headerType);
/**
* Called when RecyclerView needs a new {@link ItemViewHolder} of the given type to represent
* an item.
* <p>
* This new ViewHolder should be constructed with a new View that can represent the items
* of the given type. You can either create a new View manually or inflate it from an XML
* layout file.
* <p>
* The new ViewHolder will be used to display items of the adapter using
* {@link #onBindItemViewHolder(ItemViewHolder, int, int)}. Since it will be re-used to display
* different items in the data set, it is a good idea to cache references to sub views of
* the View to avoid unnecessary {@link View#findViewById(int)} calls.
*
* @param parent The ViewGroup into which the new View will be added after it is bound to
* an adapter position.
* @param itemType The view type of the new View.
*
* @return A new ViewHolder that holds a View of the given view type.
* @see #getSectionItemViewType(int, int)
* @see #onBindItemViewHolder(ItemViewHolder, int, int)
*/
public abstract ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int itemType);
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the {@link HeaderViewHolder#itemView} to reflect the header at the given
* position.
* <p>
* Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
* again if the position of the header changes in the data set unless the header itself is
* invalidated or the new position cannot be determined. For this reason, you should only
* use the <code>section</code> parameter while acquiring the
* related header data inside this method and should not keep a copy of it. If you need the
* position of a header later on (e.g. in a click listener), use
* {@link HeaderViewHolder#getAdapterPosition()} which will have the updated adapter
* position. Then you can use {@link #getAdapterPositionSection(int)} to get section index.
*
*
* @param viewHolder The ViewHolder which should be updated to represent the contents of the
* header at the given position in the data set.
* @param section The index of the section.
*/
public abstract void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int section);
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the {@link ItemViewHolder#itemView} to reflect the item at the given
* position.
* <p>
* Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
* again if the position of the item changes in the data set unless the item itself is
* invalidated or the new position cannot be determined. For this reason, you should only
* use the <code>offset</code> and <code>section</code> parameters while acquiring the
* related data item inside this method and should not keep a copy of it. If you need the
* position of an item later on (e.g. in a click listener), use
* {@link ItemViewHolder#getAdapterPosition()} which will have the updated adapter
* position. Then you can use {@link #getAdapterPositionSection(int)} and
* {@link #getItemSectionOffset(int, int)}
*
*
* @param viewHolder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param section The index of the section.
* @param offset The position of the item within the section.
*/
public abstract void onBindItemViewHolder(ItemViewHolder viewHolder, int section, int offset);
// Notify
/**
* Notify any registered observers that the data set has changed.
*
* <p>There are two different classes of data change events, item changes and structural
* changes. Item changes are when a single item has its data updated but no positional
* changes have occurred. Structural changes are when items are inserted, removed or moved
* within the data set.</p>
*
* <p>This event does not specify what about the data set has changed, forcing
* any observers to assume that all existing items and structure may no longer be valid.
* LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
*
* <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
* for adapters that report that they have {@link #hasStableIds() stable IDs} when
* this method is used. This can help for the purposes of animation and visual
* object persistence but individual item views will still need to be rebound
* and relaid out.</p>
*
* <p>If you are writing an adapter it will always be more efficient to use the more
* specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
* as a last resort.</p>
*
* @see #notifySectionDataSetChanged(int)
* @see #notifySectionHeaderChanged(int)
* @see #notifySectionItemChanged(int, int)
* @see #notifySectionInserted(int)
* @see #notifySectionItemInserted(int, int)
* @see #notifySectionItemRangeInserted(int, int, int)
* @see #notifySectionRemoved(int)
* @see #notifySectionItemRemoved(int, int)
* @see #notifySectionItemRangeRemoved(int, int, int)
*/
public void notifyAllSectionsDataSetChanged() {
calculateSections();
notifyDataSetChanged();
}
public void notifySectionDataSetChanged(int section) {
calculateSections();
if (mSections == null) {
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
notifyItemRangeChanged(sectionObject.position, sectionObject.length);
}
}
public void notifySectionHeaderChanged(int section) {
calculateSections();
if (mSections == null) {
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
notifyItemRangeChanged(sectionObject.position, 1);
}
}
public void notifySectionItemChanged(int section, int position) {
calculateSections();
if (mSections == null) {
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
if (position >= sectionObject.itemNumber) {
throw new IndexOutOfBoundsException("Invalid index " + position + ", size is " + sectionObject.itemNumber);
}
notifyItemChanged(sectionObject.position + position + 1);
}
}
public void notifySectionInserted(int section) {
calculateSections();
if (mSections == null) {
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
notifyItemRangeInserted(sectionObject.position, sectionObject.length);
}
}
public void notifySectionItemInserted(int section, int position) {
calculateSections();
if (mSections == null) {
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
if (position < 0 || position >= sectionObject.itemNumber) {
throw new IndexOutOfBoundsException("Invalid index " + position + ", size is " + sectionObject.itemNumber);
}
notifyItemInserted(sectionObject.position + position + 1);
}
}
public void notifySectionItemRangeInserted(int section, int position, int count) {
calculateSections();
if (mSections == null) {
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
if (position < 0 || position >= sectionObject.itemNumber) {
throw new IndexOutOfBoundsException("Invalid index " + position + ", size is " + sectionObject.itemNumber);
}
if (position + count > sectionObject.itemNumber) {
throw new IndexOutOfBoundsException("Invalid index " + (position + count) + ", size is " + sectionObject.itemNumber);
}
notifyItemRangeInserted(sectionObject.position + position + 1, count);
}
}
public void notifySectionRemoved(int section) {
if (mSections == null) {
calculateSections();
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
calculateSections();
notifyItemRangeRemoved(sectionObject.position, sectionObject.length);
}
}
public void notifySectionItemRemoved(int section, int position) {
if (mSections == null) {
calculateSections();
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
if (position < 0 || position >= sectionObject.itemNumber) {
throw new IndexOutOfBoundsException("Invalid index " + position + ", size is " + sectionObject.itemNumber);
}
calculateSections();
notifyItemRemoved(sectionObject.position + position + 1);
}
}
public void notifySectionItemRangeRemoved(int section, int position, int count) {
if (mSections == null) {
calculateSections();
notifyAllSectionsDataSetChanged();
}
else {
final Section sectionObject = mSections.get(section);
if (position < 0 || position >= sectionObject.itemNumber) {
throw new IndexOutOfBoundsException("Invalid index " + position + ", size is " + sectionObject.itemNumber);
}
if (position + count > sectionObject.itemNumber) {
throw new IndexOutOfBoundsException("Invalid index " + (position + count) + ", size is " + sectionObject.itemNumber);
}
calculateSections();
notifyItemRangeRemoved(sectionObject.position + position + 1, count);
}
}
}