Browse Source
Update post view layout and a height related bug. Use exo's internal layout for controls.
renovate/org.robolectric-robolectric-4.x
Update post view layout and a height related bug. Use exo's internal layout for controls.
renovate/org.robolectric-robolectric-4.x
Ammar Githam
4 years ago
20 changed files with 1175 additions and 703 deletions
-
3app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemAnimatedMediaViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaShareViewHolder.java
-
9app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java
-
9app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemStoryShareViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemXmaViewHolder.java
-
157app/src/main/java/awais/instagrabber/customviews/FormattedNumberTextView.java
-
24app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java
-
320app/src/main/java/awais/instagrabber/customviews/helpers/ChangeText.java
-
351app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
-
91app/src/main/java/awais/instagrabber/utils/NullSafePair.java
-
75app/src/main/java/awais/instagrabber/utils/NumberUtils.java
-
4app/src/main/res/drawable/ic_bookmark.xml
-
10app/src/main/res/drawable/ic_round_bookmark_border_24.xml
-
395app/src/main/res/layout/dialog_post_view.xml
-
107app/src/main/res/layout/layout_exo_custom_controls.xml
-
203app/src/main/res/layout/layout_post_view_bottom.xml
-
8app/src/main/res/layout/layout_video_player_with_thumbnail.xml
-
4app/src/main/res/values/drawables.xml
@ -0,0 +1,157 @@ |
|||
package awais.instagrabber.customviews; |
|||
|
|||
import android.content.Context; |
|||
import android.util.AttributeSet; |
|||
import android.util.Log; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.appcompat.widget.AppCompatTextView; |
|||
import androidx.transition.TransitionManager; |
|||
|
|||
import java.time.Duration; |
|||
|
|||
import awais.instagrabber.customviews.helpers.ChangeText; |
|||
import awais.instagrabber.utils.NumberUtils; |
|||
|
|||
public class FormattedNumberTextView extends AppCompatTextView { |
|||
private static final String TAG = FormattedNumberTextView.class.getSimpleName(); |
|||
private static final ChangeText TRANSITION = new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); |
|||
|
|||
private long number = Long.MIN_VALUE; |
|||
private boolean showAbbreviation = true; |
|||
private boolean animateChanges = true; |
|||
private boolean toggleOnClick = true; |
|||
private boolean autoToggleToAbbreviation = true; |
|||
private long autoToggleTimeoutMs = Duration.ofSeconds(2).toMillis(); |
|||
private boolean initDone = false; |
|||
|
|||
|
|||
public FormattedNumberTextView(@NonNull final Context context) { |
|||
super(context); |
|||
init(); |
|||
} |
|||
|
|||
public FormattedNumberTextView(@NonNull final Context context, @Nullable final AttributeSet attrs) { |
|||
super(context, attrs); |
|||
init(); |
|||
} |
|||
|
|||
public FormattedNumberTextView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { |
|||
super(context, attrs, defStyleAttr); |
|||
init(); |
|||
} |
|||
|
|||
private void init() { |
|||
if (initDone) return; |
|||
setupClickToggle(); |
|||
initDone = true; |
|||
} |
|||
|
|||
private void setupClickToggle() { |
|||
setOnClickListener(null); |
|||
} |
|||
|
|||
private OnClickListener getWrappedClickListener(@Nullable final OnClickListener l) { |
|||
if (!toggleOnClick) { |
|||
return l; |
|||
} |
|||
return v -> { |
|||
toggleAbbreviation(); |
|||
if (l != null) { |
|||
l.onClick(this); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
public void setNumber(final long number) { |
|||
if (this.number == number) return; |
|||
this.number = number; |
|||
format(); |
|||
} |
|||
|
|||
public void clearNumber() { |
|||
if (number == Long.MIN_VALUE) return; |
|||
number = Long.MIN_VALUE; |
|||
format(); |
|||
} |
|||
|
|||
public void setShowAbbreviation(final boolean showAbbreviation) { |
|||
if (this.showAbbreviation && showAbbreviation) return; |
|||
this.showAbbreviation = showAbbreviation; |
|||
format(); |
|||
} |
|||
|
|||
public boolean isShowAbbreviation() { |
|||
return showAbbreviation; |
|||
} |
|||
|
|||
private void toggleAbbreviation() { |
|||
if (number == Long.MIN_VALUE) return; |
|||
setShowAbbreviation(!showAbbreviation); |
|||
} |
|||
|
|||
public void setToggleOnClick(final boolean toggleOnClick) { |
|||
this.toggleOnClick = toggleOnClick; |
|||
} |
|||
|
|||
public boolean isToggleOnClick() { |
|||
return toggleOnClick; |
|||
} |
|||
|
|||
public void setAutoToggleToAbbreviation(final boolean autoToggleToAbbreviation) { |
|||
this.autoToggleToAbbreviation = autoToggleToAbbreviation; |
|||
} |
|||
|
|||
public boolean isAutoToggleToAbbreviation() { |
|||
return autoToggleToAbbreviation; |
|||
} |
|||
|
|||
public void setAutoToggleTimeoutMs(final long autoToggleTimeoutMs) { |
|||
this.autoToggleTimeoutMs = autoToggleTimeoutMs; |
|||
} |
|||
|
|||
public long getAutoToggleTimeoutMs() { |
|||
return autoToggleTimeoutMs; |
|||
} |
|||
|
|||
public void setAnimateChanges(final boolean animateChanges) { |
|||
this.animateChanges = animateChanges; |
|||
} |
|||
|
|||
public boolean isAnimateChanges() { |
|||
return animateChanges; |
|||
} |
|||
|
|||
@Override |
|||
public void setOnClickListener(@Nullable final OnClickListener l) { |
|||
super.setOnClickListener(getWrappedClickListener(l)); |
|||
} |
|||
|
|||
private void format() { |
|||
post(() -> { |
|||
if (animateChanges) { |
|||
try { |
|||
TransitionManager.beginDelayedTransition((ViewGroup) getParent(), TRANSITION); |
|||
} catch (Exception e) { |
|||
Log.e(TAG, "format: ", e); |
|||
} |
|||
} |
|||
if (number == Long.MIN_VALUE) { |
|||
setText(null); |
|||
return; |
|||
} |
|||
if (showAbbreviation) { |
|||
setText(NumberUtils.abbreviate(number)); |
|||
return; |
|||
} |
|||
setText(String.valueOf(number)); |
|||
if (autoToggleToAbbreviation) { |
|||
getHandler().postDelayed(() -> setShowAbbreviation(true), autoToggleTimeoutMs); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,320 @@ |
|||
package awais.instagrabber.customviews.helpers; |
|||
|
|||
/* |
|||
* Copyright (C) 2013 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
import android.animation.Animator; |
|||
import android.animation.AnimatorListenerAdapter; |
|||
import android.animation.AnimatorSet; |
|||
import android.animation.ValueAnimator; |
|||
import android.graphics.Color; |
|||
import android.util.Log; |
|||
import android.view.ViewGroup; |
|||
import android.widget.EditText; |
|||
import android.widget.TextView; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.transition.Transition; |
|||
import androidx.transition.TransitionListenerAdapter; |
|||
import androidx.transition.TransitionValues; |
|||
|
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
|
|||
/** |
|||
* This transition tracks changes to the text in TextView targets. If the text |
|||
* changes between the start and end scenes, the transition ensures that the |
|||
* starting text stays until the transition ends, at which point it changes |
|||
* to the end text. This is useful in situations where you want to resize a |
|||
* text view to its new size before displaying the text that goes there. |
|||
*/ |
|||
public class ChangeText extends Transition { |
|||
private static final String LOG_TAG = "TextChange"; |
|||
private static final String PROPNAME_TEXT = "android:textchange:text"; |
|||
private static final String PROPNAME_TEXT_SELECTION_START = |
|||
"android:textchange:textSelectionStart"; |
|||
private static final String PROPNAME_TEXT_SELECTION_END = |
|||
"android:textchange:textSelectionEnd"; |
|||
private static final String PROPNAME_TEXT_COLOR = "android:textchange:textColor"; |
|||
private int mChangeBehavior = CHANGE_BEHAVIOR_KEEP; |
|||
private boolean crossFade; |
|||
/** |
|||
* Flag specifying that the text in affected/changing TextView targets will keep |
|||
* their original text during the transition, setting it to the final text when |
|||
* the transition ends. This is the default behavior. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_KEEP = 0; |
|||
/** |
|||
* Flag specifying that the text changing animation should first fade |
|||
* out the original text completely. The new text is set on the target |
|||
* view at the end of the fade-out animation. This transition is typically |
|||
* used with a later {@link #CHANGE_BEHAVIOR_IN} transition, allowing more |
|||
* flexibility than the {@link #CHANGE_BEHAVIOR_OUT_IN} by allowing other |
|||
* transitions to be run sequentially or in parallel with these fades. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_OUT = 1; |
|||
/** |
|||
* Flag specifying that the text changing animation should fade in the |
|||
* end text into the affected target view(s). This transition is typically |
|||
* used in conjunction with an earlier {@link #CHANGE_BEHAVIOR_OUT} |
|||
* transition, possibly with other transitions running as well, such as |
|||
* a sequence to fade out, then resize the view, then fade in. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_IN = 2; |
|||
/** |
|||
* Flag specifying that the text changing animation should first fade |
|||
* out the original text completely and then fade in the |
|||
* new text. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_OUT_IN = 3; |
|||
private static final String[] sTransitionProperties = { |
|||
PROPNAME_TEXT, |
|||
PROPNAME_TEXT_SELECTION_START, |
|||
PROPNAME_TEXT_SELECTION_END |
|||
}; |
|||
|
|||
/** |
|||
* Sets the type of changing animation that will be run, one of |
|||
* {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT}, |
|||
* {@link #CHANGE_BEHAVIOR_IN}, and {@link #CHANGE_BEHAVIOR_OUT_IN}. |
|||
* |
|||
* @param changeBehavior The type of fading animation to use when this |
|||
* transition is run. |
|||
* @return this textChange object. |
|||
*/ |
|||
public ChangeText setChangeBehavior(int changeBehavior) { |
|||
if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) { |
|||
mChangeBehavior = changeBehavior; |
|||
} |
|||
return this; |
|||
} |
|||
|
|||
public ChangeText setCrossFade(final boolean crossFade) { |
|||
this.crossFade = crossFade; |
|||
return this; |
|||
} |
|||
|
|||
@Override |
|||
public String[] getTransitionProperties() { |
|||
return sTransitionProperties; |
|||
} |
|||
|
|||
/** |
|||
* Returns the type of changing animation that will be run. |
|||
* |
|||
* @return either {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT}, |
|||
* {@link #CHANGE_BEHAVIOR_IN}, or {@link #CHANGE_BEHAVIOR_OUT_IN}. |
|||
*/ |
|||
public int getChangeBehavior() { |
|||
return mChangeBehavior; |
|||
} |
|||
|
|||
private void captureValues(TransitionValues transitionValues) { |
|||
if (transitionValues.view instanceof TextView) { |
|||
TextView textview = (TextView) transitionValues.view; |
|||
transitionValues.values.put(PROPNAME_TEXT, textview.getText()); |
|||
if (textview instanceof EditText) { |
|||
transitionValues.values.put(PROPNAME_TEXT_SELECTION_START, |
|||
textview.getSelectionStart()); |
|||
transitionValues.values.put(PROPNAME_TEXT_SELECTION_END, |
|||
textview.getSelectionEnd()); |
|||
} |
|||
if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { |
|||
transitionValues.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void captureStartValues(@NonNull TransitionValues transitionValues) { |
|||
captureValues(transitionValues); |
|||
} |
|||
|
|||
@Override |
|||
public void captureEndValues(@NonNull TransitionValues transitionValues) { |
|||
captureValues(transitionValues); |
|||
} |
|||
|
|||
@Override |
|||
public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues, |
|||
TransitionValues endValues) { |
|||
if (startValues == null || endValues == null || |
|||
!(startValues.view instanceof TextView) || !(endValues.view instanceof TextView)) { |
|||
return null; |
|||
} |
|||
final TextView view = (TextView) endValues.view; |
|||
Map<String, Object> startVals = startValues.values; |
|||
Map<String, Object> endVals = endValues.values; |
|||
final CharSequence startText = startVals.get(PROPNAME_TEXT) != null ? |
|||
(CharSequence) startVals.get(PROPNAME_TEXT) : ""; |
|||
final CharSequence endText = endVals.get(PROPNAME_TEXT) != null ? |
|||
(CharSequence) endVals.get(PROPNAME_TEXT) : ""; |
|||
final int startSelectionStart, startSelectionEnd, endSelectionStart, endSelectionEnd; |
|||
if (view instanceof EditText) { |
|||
startSelectionStart = startVals.get(PROPNAME_TEXT_SELECTION_START) != null ? |
|||
(Integer) startVals.get(PROPNAME_TEXT_SELECTION_START) : -1; |
|||
startSelectionEnd = startVals.get(PROPNAME_TEXT_SELECTION_END) != null ? |
|||
(Integer) startVals.get(PROPNAME_TEXT_SELECTION_END) : startSelectionStart; |
|||
endSelectionStart = endVals.get(PROPNAME_TEXT_SELECTION_START) != null ? |
|||
(Integer) endVals.get(PROPNAME_TEXT_SELECTION_START) : -1; |
|||
endSelectionEnd = endVals.get(PROPNAME_TEXT_SELECTION_END) != null ? |
|||
(Integer) endVals.get(PROPNAME_TEXT_SELECTION_END) : endSelectionStart; |
|||
} else { |
|||
startSelectionStart = startSelectionEnd = endSelectionStart = endSelectionEnd = -1; |
|||
} |
|||
if (!Objects.equals(startText, endText)) { |
|||
final int startColor; |
|||
final int endColor; |
|||
if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { |
|||
view.setText(startText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), startSelectionStart, startSelectionEnd); |
|||
} |
|||
} |
|||
Animator anim; |
|||
if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) { |
|||
startColor = endColor = 0; |
|||
anim = ValueAnimator.ofFloat(0, 1); |
|||
anim.addListener(new AnimatorListenerAdapter() { |
|||
@Override |
|||
public void onAnimationEnd(Animator animation) { |
|||
if (Objects.equals(startText, view.getText())) { |
|||
// Only set if it hasn't been changed since anim started |
|||
view.setText(endText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), endSelectionStart, endSelectionEnd); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} else { |
|||
startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR); |
|||
endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR); |
|||
// Fade out start text |
|||
ValueAnimator outAnim = null, inAnim = null; |
|||
if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || |
|||
mChangeBehavior == CHANGE_BEHAVIOR_OUT) { |
|||
outAnim = ValueAnimator.ofInt(Color.alpha(startColor), 0); |
|||
outAnim.addUpdateListener(animation -> { |
|||
int currAlpha = (Integer) animation.getAnimatedValue(); |
|||
view.setTextColor(currAlpha << 24 | startColor & 0xffffff); |
|||
}); |
|||
outAnim.addListener(new AnimatorListenerAdapter() { |
|||
@Override |
|||
public void onAnimationEnd(Animator animation) { |
|||
if (Objects.equals(startText, view.getText())) { |
|||
// Only set if it hasn't been changed since anim started |
|||
view.setText(endText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), endSelectionStart, |
|||
endSelectionEnd); |
|||
} |
|||
} |
|||
// restore opaque alpha and correct end color |
|||
view.setTextColor(endColor); |
|||
} |
|||
}); |
|||
} |
|||
if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || |
|||
mChangeBehavior == CHANGE_BEHAVIOR_IN) { |
|||
inAnim = ValueAnimator.ofInt(0, Color.alpha(endColor)); |
|||
inAnim.addUpdateListener(animation -> { |
|||
int currAlpha = (Integer) animation.getAnimatedValue(); |
|||
view.setTextColor(currAlpha << 24 | endColor & 0xffffff); |
|||
}); |
|||
inAnim.addListener(new AnimatorListenerAdapter() { |
|||
@Override |
|||
public void onAnimationCancel(Animator animation) { |
|||
// restore opaque alpha and correct end color |
|||
view.setTextColor(endColor); |
|||
} |
|||
}); |
|||
} |
|||
if (outAnim != null && inAnim != null) { |
|||
anim = new AnimatorSet(); |
|||
final AnimatorSet animatorSet = (AnimatorSet) anim; |
|||
if (crossFade) { |
|||
animatorSet.playTogether(outAnim, inAnim); |
|||
} else { |
|||
animatorSet.playSequentially(outAnim, inAnim); |
|||
} |
|||
} else if (outAnim != null) { |
|||
anim = outAnim; |
|||
} else { |
|||
// Must be an in-only animation |
|||
anim = inAnim; |
|||
} |
|||
} |
|||
TransitionListener transitionListener = new TransitionListenerAdapter() { |
|||
int mPausedColor = 0; |
|||
|
|||
@Override |
|||
public void onTransitionPause(@NonNull Transition transition) { |
|||
if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { |
|||
view.setText(endText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), endSelectionStart, endSelectionEnd); |
|||
} |
|||
} |
|||
if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { |
|||
mPausedColor = view.getCurrentTextColor(); |
|||
view.setTextColor(endColor); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onTransitionResume(@NonNull Transition transition) { |
|||
if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { |
|||
view.setText(startText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), startSelectionStart, startSelectionEnd); |
|||
} |
|||
} |
|||
if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { |
|||
view.setTextColor(mPausedColor); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onTransitionEnd(Transition transition) { |
|||
transition.removeListener(this); |
|||
} |
|||
}; |
|||
addListener(transitionListener); |
|||
if (BuildConfig.DEBUG) { |
|||
Log.d(LOG_TAG, "createAnimator returning " + anim); |
|||
} |
|||
return anim; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
private void setSelection(EditText editText, int start, int end) { |
|||
if (start >= 0 && end >= 0) { |
|||
editText.setSelection(start, end); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,91 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
/* |
|||
* Copyright (C) 2009 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.util.ObjectsCompat; |
|||
|
|||
/** |
|||
* Container to ease passing around a tuple of two objects. This object provides a sensible |
|||
* implementation of equals(), returning true if equals() is true on each of the contained |
|||
* objects. |
|||
*/ |
|||
public class NullSafePair<F, S> { |
|||
public final @NonNull |
|||
F first; |
|||
public final @NonNull |
|||
S second; |
|||
|
|||
/** |
|||
* Constructor for a Pair. |
|||
* |
|||
* @param first the first object in the Pair |
|||
* @param second the second object in the pair |
|||
*/ |
|||
public NullSafePair(@NonNull F first, @NonNull S second) { |
|||
this.first = first; |
|||
this.second = second; |
|||
} |
|||
|
|||
/** |
|||
* Checks the two objects for equality by delegating to their respective |
|||
* {@link Object#equals(Object)} methods. |
|||
* |
|||
* @param o the {@link androidx.core.util.Pair} to which this one is to be checked for equality |
|||
* @return true if the underlying objects of the Pair are both considered |
|||
* equal |
|||
*/ |
|||
@Override |
|||
public boolean equals(Object o) { |
|||
if (!(o instanceof androidx.core.util.Pair)) { |
|||
return false; |
|||
} |
|||
androidx.core.util.Pair<?, ?> p = (androidx.core.util.Pair<?, ?>) o; |
|||
return ObjectsCompat.equals(p.first, first) && ObjectsCompat.equals(p.second, second); |
|||
} |
|||
|
|||
/** |
|||
* Compute a hash code using the hash codes of the underlying objects |
|||
* |
|||
* @return a hashcode of the Pair |
|||
*/ |
|||
@Override |
|||
public int hashCode() { |
|||
return first.hashCode() ^ second.hashCode(); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public String toString() { |
|||
return "Pair{" + first + " " + second + "}"; |
|||
} |
|||
|
|||
/** |
|||
* Convenience method for creating an appropriately typed pair. |
|||
* |
|||
* @param a the first object in the Pair |
|||
* @param b the second object in the pair |
|||
* @return a Pair that is templatized with the types of a and b |
|||
*/ |
|||
@NonNull |
|||
public static <A, B> androidx.core.util.Pair<A, B> create(@Nullable A a, @Nullable B b) { |
|||
return new androidx.core.util.Pair<A, B>(a, b); |
|||
} |
|||
} |
|||
|
@ -1,9 +1,9 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:tint="?colorControlNormal" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="#333333"> |
|||
android:viewportHeight="24"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z" /> |
|||
|
@ -0,0 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="?attr/colorControlNormal"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M17,3L7,3c-1.1,0 -2,0.9 -2,2v16l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,6c0,-0.55 0.45,-1 1,-1h8c0.55,0 1,0.45 1,1v12z"/> |
|||
</vector> |
@ -0,0 +1,203 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<merge xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/buttons_top_barrier" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
app:barrierAllowsGoneWidgets="true" |
|||
app:barrierDirection="bottom" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/like" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="36dp" |
|||
android:layout_height="40dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_not_liked" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="22dp" |
|||
app:layout_constraintBottom_toTopOf="@id/buttons_bottom_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/likes_count" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_top_barrier" |
|||
app:layout_constraintVertical_bias="0" |
|||
app:layout_constraintVertical_chainStyle="packed" |
|||
tools:visibility="visible" /> |
|||
|
|||
<awais.instagrabber.customviews.FormattedNumberTextView |
|||
android:id="@+id/likes_count" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="0dp" |
|||
android:layout_marginEnd="0dp" |
|||
android:background="@drawable/background_grey_ripple" |
|||
android:gravity="center_vertical" |
|||
android:maxWidth="100dp" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" |
|||
android:textColor="?android:attr/textColorSecondary" |
|||
app:layout_constraintBottom_toBottomOf="@id/like" |
|||
app:layout_constraintEnd_toStartOf="@id/comment" |
|||
app:layout_constraintStart_toEndOf="@id/like" |
|||
app:layout_constraintTop_toTopOf="@id/like" |
|||
tools:text="9.9k" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/comment" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="36dp" |
|||
android:layout_height="40dp" |
|||
android:layout_marginStart="8dp" |
|||
app:icon="@drawable/ic_outline_comments_24" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="22dp" |
|||
app:layout_constraintBottom_toTopOf="@id/buttons_bottom_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/comments_count" |
|||
app:layout_constraintStart_toEndOf="@id/likes_count" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_top_barrier" /> |
|||
|
|||
<awais.instagrabber.customviews.FormattedNumberTextView |
|||
android:id="@+id/comments_count" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="0dp" |
|||
android:background="@drawable/background_grey_ripple" |
|||
android:ellipsize="end" |
|||
android:gravity="center_vertical" |
|||
android:maxWidth="100dp" |
|||
android:maxLines="1" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" |
|||
android:textColor="?android:attr/textColorSecondary" |
|||
app:layout_constraintBottom_toTopOf="@id/buttons_bottom_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/space_1" |
|||
app:layout_constraintStart_toEndOf="@id/comment" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_top_barrier" |
|||
tools:text="9999999999999999999999999999999999999999" /> |
|||
|
|||
<Space |
|||
android:id="@+id/space_1" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
app:layout_constraintBottom_toTopOf="@id/buttons_bottom_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/share" |
|||
app:layout_constraintStart_toEndOf="@id/comments_count" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_top_barrier" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/share" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="48dp" |
|||
android:layout_height="40dp" |
|||
app:icon="?attr/actionModeShareDrawable" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="18dp" |
|||
app:layout_constraintBottom_toTopOf="@id/buttons_bottom_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/save" |
|||
app:layout_constraintStart_toEndOf="@id/space_1" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_top_barrier" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/save" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="48dp" |
|||
android:layout_height="40dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_round_bookmark_border_24" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="18dp" |
|||
app:layout_constraintBottom_toTopOf="@id/buttons_bottom_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/download" |
|||
app:layout_constraintStart_toEndOf="@id/share" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_top_barrier" |
|||
tools:visibility="visible" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/download" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="48dp" |
|||
android:layout_height="40dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_download" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="18dp" |
|||
app:layout_constraintBottom_toTopOf="@id/buttons_bottom_barrier" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/save" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_top_barrier" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/buttons_bottom_barrier" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
app:barrierAllowsGoneWidgets="true" |
|||
app:barrierDirection="top" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/date" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:padding="8dp" |
|||
app:layout_constraintBottom_toTopOf="@id/caption_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/views_count" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_bottom_barrier" |
|||
tools:layout_constraintVertical_chainStyle="packed" |
|||
tools:text="2020-11-07 11:18:55" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/views_count" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:padding="8dp" |
|||
app:layout_constraintBottom_toBottomOf="@id/date" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/date" |
|||
app:layout_constraintTop_toTopOf="@id/date" |
|||
tools:text="9999999999 views" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/caption_barrier" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
app:barrierAllowsGoneWidgets="true" |
|||
app:barrierDirection="bottom" /> |
|||
|
|||
<awais.instagrabber.customviews.RamboTextViewV2 |
|||
android:id="@+id/caption" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="bottom" |
|||
android:background="@null" |
|||
android:clickable="true" |
|||
android:focusable="true" |
|||
android:padding="8dp" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
app:layout_constraintBottom_toTopOf="@id/translate" |
|||
app:layout_constraintTop_toBottomOf="@id/caption_barrier" |
|||
app:layout_constraintVertical_bias="0" |
|||
app:layout_constraintVertical_chainStyle="packed" |
|||
tools:text="Text text text Text text text Text text text Text text text Text text text" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/translate" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:background="@null" |
|||
android:gravity="center_vertical" |
|||
android:padding="8dp" |
|||
android:text="@string/translate_caption" |
|||
android:textColor="@color/blue_600" |
|||
android:textSize="16sp" |
|||
android:visibility="visible" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/caption" /> |
|||
</merge> |
@ -1,5 +1,5 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<resources> |
|||
<drawable name="exo_styled_controls_play">@drawable/ic_play_arrow_24</drawable> |
|||
<drawable name="exo_styled_controls_pause">@drawable/ic_pause_24</drawable> |
|||
<!--<drawable name="exo_styled_controls_play">@drawable/ic_play_arrow_24</drawable>--> |
|||
<!--<drawable name="exo_styled_controls_pause">@drawable/ic_pause_24</drawable>--> |
|||
</resources> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue