Dynamic playlists with Exoplayer 2 - android

I'd like to use ExoPlayer2 with playlists having possibility to dinamically change the tracks (add or remove them from playlist) and change the loop settings.
Since ConcatenatingMediaSource has static arrays (and not lists), I'm implementing a DynamicMediaSource, like Concatenating one but with lists instead of arrays and one mode method addSource to add one more media source to the list.
public void addSource(MediaSource mediaSource) {
this.mediaSources.add(mediaSource);
duplicateFlags = buildDuplicateFlags(this.mediaSources);
if(!mediaSources.isEmpty())
prepareSource(mediaSources.size() -1);
else
prepareSource(0);
}
When I invoke addSource
MediaSource ms = buildMediaSource(mynewuri, null);
mediaSource.addSource(ms);
the track is added to the arrays but it seems something is missing because I always obtain ArrayOutOfBoundsException in createPeriod method.
In createPeriod the method
mediaSources.get(sourceIndex)...
is trying to access the index = mediaSources.size().
Can you help me?

I eventually managed it.
It was my fault during the conversion from arrays to lists.
I had to use SparseArrays for timelines and manifests and everything began to work.
In the DynamicMediaSource simply set the following types:
private final List<MediaSource> mediaSources;
private final SparseArray<Timeline> timelines;
private final SparseArray<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
you have to use sparse arrays to set the proper values into the timelines and manifests in the method
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.put(sourceFirstIndex, sourceTimeline);
manifests.put(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.put(i, sourceTimeline);
manifests.put(i, sourceManifest);
}
}
for(int i= 0; i<mediaSources.size(); i++){
if(timelines.get(i) == null){
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(new ArrayList(asList(timelines)));
listener.onSourceInfoRefreshed(timeline, new ArrayList(asList(manifests)));
}
Here is the complete code of DynamicMediaSource class:
public final class DynamicMediaSource implements MediaSource {
private static final String TAG = "DynamicSource";
private final List<MediaSource> mediaSources;
private final List<Timeline> timelines;
private final List<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
private Listener listener;
private DynamicTimeline timeline;
/**
* #param mediaSources The {#link MediaSource}s to concatenate. It is valid for the same
* {#link MediaSource} instance to be present more than once in the array.
*/
public DynamicMediaSource(MediaSource... mediaSources) {
this.mediaSources = new ArrayList<MediaSource>(Arrays.asList(mediaSources));
timelines = new ArrayList<Timeline>();
manifests = new ArrayList<Object>();
sourceIndexByMediaPeriod = new HashMap<>();
duplicateFlags = buildDuplicateFlags(this.mediaSources);
}
public void addSource(MediaSource mediaSource) {
this.mediaSources.add(mediaSource);
duplicateFlags = buildDuplicateFlags(this.mediaSources);
/*if(!mediaSources.isEmpty())
prepareSource(mediaSources.size() -1);
else
prepareSource(0);*/
}
#Override
public void prepareSource(Listener listener) {
this.listener = listener;
for (int i = 0; i < mediaSources.size(); i++) {
prepareSource(i);
/*if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
final int index = i;
mediaSources.get(i).prepareSource(new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}*/
}
}
private void prepareSource(int sourceindex) {
if (duplicateFlags.get(sourceindex) == null || !duplicateFlags.get(sourceindex)) {
final int index = sourceindex;
mediaSources.get(sourceindex).prepareSource(new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}
}
#Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
for (int i = 0; i < mediaSources.size(); i++) {
if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
mediaSources.get(i).maybeThrowSourceInfoRefreshError();
}
}
}
#Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
long positionUs) {
int sourceIndex = timeline.getSourceIndexForPeriod(index);
int periodIndexInSource = index - timeline.getFirstPeriodIndexInSource(sourceIndex);
MediaPeriod mediaPeriod = mediaSources.get(sourceIndex).createPeriod(periodIndexInSource, callback,
allocator, positionUs);
sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
return mediaPeriod;
}
#Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
sourceIndexByMediaPeriod.remove(mediaPeriod);
mediaSources.get(sourceIndex).releasePeriod(mediaPeriod);
}
#Override
public void releaseSource() {
for (int i = 0; i < mediaSources.size(); i++) {
if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
mediaSources.get(i).releaseSource();
}
}
}
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.add(sourceFirstIndex, sourceTimeline);
manifests.add(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.add(i, sourceTimeline);
manifests.add(i, sourceManifest);
}
}
for (Timeline timeline : timelines) {
if (timeline == null) {
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(new ArrayList(timelines));
listener.onSourceInfoRefreshed(timeline, new ArrayList(manifests));
}
private static SparseArray<Boolean> buildDuplicateFlags(List<MediaSource> mediaSources) {
SparseArray<Boolean> duplicateFlags = new SparseArray<Boolean>();
IdentityHashMap<MediaSource, Void> sources = new IdentityHashMap<>(mediaSources.size());
for (int i = 0; i < mediaSources.size(); i++) {
MediaSource mediaSource = mediaSources.get(i);
if (!sources.containsKey(mediaSource)) {
sources.put(mediaSource, null);
} else {
duplicateFlags.setValueAt(i, true);
}
}
return duplicateFlags;
}
/**
* A {#link Timeline} that is the concatenation of one or more {#link Timeline}s.
*/
private static final class DynamicTimeline extends Timeline {
private final List<Timeline> timelines;
private final List<Integer> sourcePeriodOffsets;
private final List<Integer> sourceWindowOffsets;
public DynamicTimeline(List<Timeline> timelines) {
List<Integer> sourcePeriodOffsets = new ArrayList<>();
List<Integer> sourceWindowOffsets = new ArrayList<>();
int periodCount = 0;
int windowCount = 0;
for (Timeline timeline : timelines) {
periodCount += timeline.getPeriodCount();
windowCount += timeline.getWindowCount();
sourcePeriodOffsets.add(periodCount);
sourceWindowOffsets.add(windowCount);
}
this.timelines = timelines;
this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets;
}
#Override
public int getWindowCount() {
return sourceWindowOffsets.get(sourceWindowOffsets.size() - 1);
}
#Override
public Window getWindow(int windowIndex, Window window, boolean setIds) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
timelines.get(sourceIndex).getWindow(windowIndex - firstWindowIndexInSource, window, setIds);
window.firstPeriodIndex += firstPeriodIndexInSource;
window.lastPeriodIndex += firstPeriodIndexInSource;
return window;
}
#Override
public int getPeriodCount() {
return sourcePeriodOffsets.get(sourcePeriodOffsets.size() - 1);
}
#Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
timelines.get(sourceIndex).getPeriod(periodIndex - firstPeriodIndexInSource, period, setIds);
period.windowIndex += firstWindowIndexInSource;
if (setIds) {
period.uid = Pair.create(sourceIndex, period.uid);
}
return period;
}
#Override
public int getIndexOfPeriod(Object uid) {
if (!(uid instanceof Pair)) {
return C.INDEX_UNSET;
}
Pair<?, ?> sourceIndexAndPeriodId = (Pair<?, ?>) uid;
if (!(sourceIndexAndPeriodId.first instanceof Integer)) {
return C.INDEX_UNSET;
}
int sourceIndex = (Integer) sourceIndexAndPeriodId.first;
Object periodId = sourceIndexAndPeriodId.second;
if (sourceIndex < 0 || sourceIndex >= timelines.size()) {
return C.INDEX_UNSET;
}
int periodIndexInSource = timelines.get(sourceIndex).getIndexOfPeriod(periodId);
return periodIndexInSource == C.INDEX_UNSET ? C.INDEX_UNSET
: getFirstPeriodIndexInSource(sourceIndex) + periodIndexInSource;
}
private int getSourceIndexForPeriod(int periodIndex) {
return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
}
private int getFirstPeriodIndexInSource(int sourceIndex) {
return sourceIndex == 0 ? 0 : sourcePeriodOffsets.get(sourceIndex - 1);
}
private int getSourceIndexForWindow(int windowIndex) {
return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
}
private int getFirstWindowIndexInSource(int sourceIndex) {
return sourceIndex == 0 ? 0 : sourceWindowOffsets.get(sourceIndex - 1);
}
}
}

Related

Masked EditText with format (XXX) XXX-XXXX ext.XXXXXX in android?

How can I mask EditText with below format in android ?
(XXX) XXX-XXXX ext.XXXXXX i.e (654) 321-5846 ext.654321
Used below Custom Edit Text class
package com.test.PhoneNumberFormatter;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.TextView;
import com.test.R;
import static android.content.ContentValues.TAG;
public class MaskedEditText extends AppCompatEditText implements TextWatcher {
public static final String SPACE = " ";
private String mask;
private char charRepresentation;
private boolean keepHint;
private int[] rawToMask;
private RawText rawText;
private boolean editingBefore;
private boolean editingOnChanged;
private boolean editingAfter;
private int[] maskToRaw;
private int selection;
private boolean initialized;
private boolean ignore;
protected int maxRawLength;
private int lastValidMaskPosition;
private boolean selectionChanged;
private OnFocusChangeListener focusChangeListener;
private String allowedChars;
private String deniedChars;
public MaskedEditText(Context context) {
super(context);
init();
}
public MaskedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.MaskedEditText);
mask = attributes.getString(R.styleable.MaskedEditText_mask);
allowedChars = attributes.getString(R.styleable.MaskedEditText_allowed_chars);
deniedChars = attributes.getString(R.styleable.MaskedEditText_denied_chars);
String representation = attributes.getString(R.styleable.MaskedEditText_char_representation);
if (representation == null) {
charRepresentation = '#';
} else {
charRepresentation = representation.charAt(0);
}
keepHint = attributes.getBoolean(R.styleable.MaskedEditText_keep_hint, false);
cleanUp();
// Ignoring enter key presses
setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
switch (actionId) {
// case EditorInfo.IME_ACTION_NEXT:
// fixing actionNext
// return false;
default:
return true;
}
}
});
attributes.recycle();
}
#Override
public Parcelable onSaveInstanceState() {
final Parcelable superParcellable = super.onSaveInstanceState();
final Bundle state = new Bundle();
state.putParcelable("super", superParcellable);
state.putString("text", getRawText());
state.putBoolean("keepHint", isKeepHint());
return state;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
keepHint = bundle.getBoolean("keepHint", false);
super.onRestoreInstanceState(((Bundle) state).getParcelable("super"));
final String text = bundle.getString("text");
setText(text);
Log.d(TAG, "onRestoreInstanceState: " + text);
}
#Override
public void setText(CharSequence text, BufferType type) {
// if (text == null || text.equals("")) return;
super.setText(text, type);
}
/**
* #param listener - its onFocusChange() method will be called before performing MaskedEditText operations,
* related to this event.
*/
#Override
public void setOnFocusChangeListener(OnFocusChangeListener listener) {
focusChangeListener = listener;
}
private void cleanUp() {
initialized = false;
generatePositionArrays();
rawText = new RawText();
selection = rawToMask[0];
editingBefore = true;
editingOnChanged = true;
editingAfter = true;
if (hasHint() && rawText.length() == 0) {
this.setText(makeMaskedTextWithHint());
} else {
this.setText(makeMaskedText());
}
editingBefore = false;
editingOnChanged = false;
editingAfter = false;
maxRawLength = maskToRaw[previousValidPosition(mask.length() - 1)] + 1;
lastValidMaskPosition = findLastValidMaskPosition();
initialized = true;
super.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (focusChangeListener != null) {
focusChangeListener.onFocusChange(v, hasFocus);
}
if (hasFocus()) {
selectionChanged = false;
MaskedEditText.this.setSelection(lastValidPosition());
}
}
});
}
private int findLastValidMaskPosition() {
for (int i = maskToRaw.length - 1; i >= 0; i--) {
if (maskToRaw[i] != -1) return i;
}
throw new RuntimeException("Mask must contain at least one representation char");
}
private boolean hasHint() {
return getHint() != null;
}
public MaskedEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void setMask(String mask) {
this.mask = mask;
cleanUp();
}
public String getMask() {
return this.mask;
}
public String getRawText() {
return this.rawText.getText();
}
public void setCharRepresentation(char charRepresentation) {
this.charRepresentation = charRepresentation;
cleanUp();
}
public char getCharRepresentation() {
return this.charRepresentation;
}
/**
* Generates positions for values characters. For instance:
* Input data: mask = "+7(###)###-##-##
* After method execution:
* rawToMask = [3, 4, 5, 6, 8, 9, 11, 12, 14, 15]
* maskToRaw = [-1, -1, -1, 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, -1, 8, 9]
* charsInMask = "+7()- " (and space, yes)
*/
private void generatePositionArrays() {
int[] aux = new int[mask.length()];
maskToRaw = new int[mask.length()];
String charsInMaskAux = "";
int charIndex = 0;
for (int i = 0; i < mask.length(); i++) {
char currentChar = mask.charAt(i);
if (currentChar == charRepresentation) {
aux[charIndex] = i;
maskToRaw[i] = charIndex++;
} else {
String charAsString = Character.toString(currentChar);
if (!charsInMaskAux.contains(charAsString)) {
charsInMaskAux = charsInMaskAux.concat(charAsString);
}
maskToRaw[i] = -1;
}
}
if (charsInMaskAux.indexOf(' ') < 0) {
charsInMaskAux = charsInMaskAux + SPACE;
}
char[] charsInMask = charsInMaskAux.toCharArray();
rawToMask = new int[charIndex];
for (int i = 0; i < charIndex; i++) {
rawToMask[i] = aux[i];
}
}
private void init() {
addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
if (!editingBefore) {
editingBefore = true;
if (start > lastValidMaskPosition) {
ignore = true;
}
int rangeStart = start;
if (after == 0) {
rangeStart = erasingStart(start);
}
Range range = calculateRange(rangeStart, start + count);
if (range.getStart() != -1) {
rawText.subtractFromString(range);
}
if (count > 0) {
selection = previousValidPosition(start);
}
}
}
private int erasingStart(int start) {
while (start > 0 && maskToRaw[start] == -1) {
start--;
}
return start;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!editingOnChanged && editingBefore) {
editingOnChanged = true;
if (ignore) {
return;
}
if (count > 0) {
int startingPosition = maskToRaw[nextValidPosition(start)];
String addedString = s.subSequence(start, start + count).toString();
count = rawText.addToString(clear(addedString), startingPosition, maxRawLength);
if (initialized) {
int currentPosition;
if (startingPosition + count < rawToMask.length)
currentPosition = rawToMask[startingPosition + count];
else
currentPosition = lastValidMaskPosition + 1;
selection = nextValidPosition(currentPosition);
}
}
}
}
#Override
public void afterTextChanged(Editable s) {
if (!editingAfter && editingBefore && editingOnChanged) {
editingAfter = true;
if (hasHint() && (keepHint || rawText.length() == 0)) {
setText(makeMaskedTextWithHint());
} else {
setText(makeMaskedText());
}
selectionChanged = false;
setSelection(selection);
editingBefore = false;
editingOnChanged = false;
editingAfter = false;
ignore = false;
}
}
public boolean isKeepHint() {
return keepHint;
}
public void setKeepHint(boolean keepHint) {
this.keepHint = keepHint;
setText(getRawText());
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
// On Android 4+ this method is being called more than 1 time if there is a hint in the EditText, what moves the cursor to left
// Using the boolean var selectionChanged to limit to one execution
if (initialized) {
if (!selectionChanged) {
selStart = fixSelection(selStart);
selEnd = fixSelection(selEnd);
// exactly in this order. If getText.length() == 0 then selStart will be -1
if (selStart > getText().length()) selStart = getText().length();
if (selStart < 0) selStart = 0;
// exactly in this order. If getText.length() == 0 then selEnd will be -1
if (selEnd > getText().length()) selEnd = getText().length();
if (selEnd < 0) selEnd = 0;
setSelection(selStart, selEnd);
selectionChanged = true;
} else {
//check to see if the current selection is outside the already entered text
if (selStart > rawText.length() - 1) {
final int start = fixSelection(selStart);
final int end = fixSelection(selEnd);
if (start >= 0 && end < getText().length()) {
setSelection(start, end);
}
}
}
}
super.onSelectionChanged(selStart, selEnd);
}
private int fixSelection(int selection) {
if (selection > lastValidPosition()) {
return lastValidPosition();
} else {
return nextValidPosition(selection);
}
}
private int nextValidPosition(int currentPosition) {
while (currentPosition < lastValidMaskPosition && maskToRaw[currentPosition] == -1) {
currentPosition++;
}
if (currentPosition > lastValidMaskPosition) return lastValidMaskPosition + 1;
return currentPosition;
}
private int previousValidPosition(int currentPosition) {
while (currentPosition >= 0 && maskToRaw[currentPosition] == -1) {
currentPosition--;
if (currentPosition < 0) {
return nextValidPosition(0);
}
}
return currentPosition;
}
private int lastValidPosition() {
if (rawText.length() == maxRawLength) {
return rawToMask[rawText.length() - 1] + 1;
}
return nextValidPosition(rawToMask[rawText.length()]);
}
private String makeMaskedText() {
int maskedTextLength;
if (rawText.length() < rawToMask.length) {
maskedTextLength = rawToMask[rawText.length()];
} else {
maskedTextLength = mask.length();
}
char[] maskedText = new char[maskedTextLength]; //mask.replace(charRepresentation, ' ').toCharArray();
for (int i = 0; i < maskedText.length; i++) {
int rawIndex = maskToRaw[i];
if (rawIndex == -1) {
maskedText[i] = mask.charAt(i);
} else {
maskedText[i] = rawText.charAt(rawIndex);
}
}
return new String(maskedText);
}
private CharSequence makeMaskedTextWithHint() {
SpannableStringBuilder ssb = new SpannableStringBuilder();
int mtrv;
int maskFirstChunkEnd = rawToMask[0];
for (int i = 0; i < mask.length(); i++) {
mtrv = maskToRaw[i];
if (mtrv != -1) {
if (mtrv < rawText.length()) {
ssb.append(rawText.charAt(mtrv));
} else {
ssb.append(getHint().charAt(maskToRaw[i]));
}
} else {
ssb.append(mask.charAt(i));
}
if ((keepHint && rawText.length() < rawToMask.length && i >= rawToMask[rawText.length()])
|| (!keepHint && i >= maskFirstChunkEnd)) {
ssb.setSpan(new ForegroundColorSpan(getCurrentHintTextColor()), i, i + 1, 0);
}
}
return ssb;
}
private Range calculateRange(int start, int end) {
Range range = new Range();
for (int i = start; i <= end && i < mask.length(); i++) {
if (maskToRaw[i] != -1) {
if (range.getStart() == -1) {
range.setStart(maskToRaw[i]);
}
range.setEnd(maskToRaw[i]);
}
}
if (end == mask.length()) {
range.setEnd(rawText.length());
}
if (range.getStart() == range.getEnd() && start < end) {
int newStart = previousValidPosition(range.getStart() - 1);
if (newStart < range.getStart()) {
range.setStart(newStart);
}
}
return range;
}
private String clear(String string) {
if (deniedChars != null) {
for (char c : deniedChars.toCharArray()) {
string = string.replace(Character.toString(c), "");
}
}
if (allowedChars != null) {
StringBuilder builder = new StringBuilder(string.length());
for (char c : string.toCharArray()) {
if (allowedChars.contains(String.valueOf(c))) {
builder.append(c);
}
}
string = builder.toString();
}
return string;
}
}
In activity_main.xml
<com.test.PhoneNumberFormatter.MaskedEditText
android:id="#+id/phone_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:hint="#string/phone_number"
android:imeOptions="actionNext"
android:inputType="textPhonetic"
android:paddingBottom="#dimen/_3sdp"
android:paddingTop="#dimen/_8sdp"
android:textColorHint="#color/grey_color"
android:textSize="#dimen/_12sdp"
mask:allowed_chars="1234567890ext."
mask:keep_hint="false"
mask:mask="(###)###-#### ext.######" />
Thanks.
For format Edit-text, below things can be helpful to you.
**Step 1** : Add following line in your build.gradle file
implementation 'com.github.pinball83:masked-edittext:1.0.4'
**Step 2 :** by xml :
<com.github.pinball83.maskededittext.MaskedEditText
android:id="#+id/masked_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
app:mask="8 (***) *** **-**"
app:notMaskedSymbol="*"
app:maskIcon="#drawable/abc_ic_clear_mtrl_alpha"
app:maskIconColor="#color/colorPrimary"
/>
or by java file :
MaskedEditText maskedEditText = new MaskedEditText.Builder(context)
.mask("8 (***) *** **-**")
.notMaskedSymbol("*")
.icon(R.drawable.ic_account_circle)
.iconCallback(unmaskedText -> { //Icon click callback handler })
.build();
MaskedEditText editText = new MaskedEditText.Builder(context)
.mask("8 (***) *** **-**")
.notMaskedSymbol("*")
.build();; //set mask to "8 (***) *** **-**" and not masked symbol to "*"
Text setup and formatting
MaskedEditText editText = new MaskedEditText..Builder(context)
.mask("8 (***) *** **-**")
.notMaskedSymbol("*")
.format("[1][2][3] [4][5][6]-[7][8]-[10][9]")//set format of returned data input into MaskedEditText
.build();
editText.setMaskedText("5551235567");
for more details use following url :
https://github.com/pinball83/Masked-Edittext
Enjoy :)
Step 1 : Create editText
<EditText
android:id="#+id/masked_edit_text"
android:layout_width="yourSize"
android:layout_height="yourSize"
android:inputType="textPassword"/>
Step2:
public class CustomTransformation extends PasswordTransformationMethod {
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return new TransformedSequence(source);
}
private class TransformedSequence implements CharSequence {
private CharSequence sequence;
public TransformedSequence(CharSequence source) {
sequence = source; // Store char sequence
}
public char charAt(int index) {
return 'X'; // Replace with what you want.
}
public int length() {
return sequence.length(); // return the length
}
public CharSequence subSequence(int start, int end) {
return sequence.subSequence(start, end); //return sequence
}
}
Step 3: apply the transformation
EditText edittext = findViewById(R.id.masked_edit_text);
edittext.setTransformationMethod(new CustomTransformation());
https://github.com/mukeshsolanki/country-picker-android
try this library .
this will automatically mask your edittext with selected country number .

ExoPlayer Hls quality

I have ExoPlayer which plays HLS videos, the thing is i need to give user ability to change video quality(auto/1080/720/480).
I figured out that playing around with AdaptiveTrackSelection.Factory does set the quality, but it remains till the object is killed.
I have also tried using MappingTrackSelector, i know that my video has 4 tracks, but i did not get how to select any of it manually. Will this selection make it work?
Thanks for any ideas.
MappingTrackSelector.MappedTrackInfo trackInfo = mDefaultTrackSelector.getCurrentMappedTrackInfo();
mDefaultTrackSelector.selectTracks(
//what should go here?
, trackInfo.getTrackGroups(4));
Regarding this thread :https://github.com/google/ExoPlayer/issues/2250, I managed to change exo player video quality while playing previous one, so it does not getting in buffering instantly.
So I have next classes :
public enum HLSQuality {
Auto, Quality1080, Quality720, Quality480, NoValue
}
class HLSUtil {
private HLSUtil() {
}
#NonNull
static HLSQuality getQuality(#NonNull Format format) {
switch (format.height) {
case 1080: {
return HLSQuality.Quality1080;
}
case 720: {
return HLSQuality.Quality720;
}
case 480:
case 486: {
return HLSQuality.Quality480;
}
default: {
return HLSQuality.NoValue;
}
}
}
static boolean isQualityPlayable(#NonNull Format format) {
return format.height <= 1080;
}
}
public class ClassAdaptiveTrackSelection extends BaseTrackSelection {
public static final class Factory implements TrackSelection.Factory {
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate = 2000000;
private final int minDurationForQualityIncreaseMs = 10000;
private final int maxDurationForQualityDecreaseMs = 25000;
private final int minDurationToRetainAfterDiscardMs = 25000;
private final float bandwidthFraction = 0.75f;
private final float bufferedFractionToLiveEdgeForQualityIncrease = 0.75f;
public Factory(BandwidthMeter bandwidthMeter) {
this.bandwidthMeter = bandwidthMeter;
}
#Override
public ClassAdaptiveTrackSelection createTrackSelection(TrackGroup group, int... tracks) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality reset to Auto");
sHLSQuality = HLSQuality.Auto;
return new ClassAdaptiveTrackSelection(
group,
tracks,
bandwidthMeter,
maxInitialBitrate,
minDurationForQualityIncreaseMs,
maxDurationForQualityDecreaseMs,
minDurationToRetainAfterDiscardMs,
bandwidthFraction,
bufferedFractionToLiveEdgeForQualityIncrease
);
}
}
private static HLSQuality sHLSQuality = HLSQuality.Auto;
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate;
private final long minDurationForQualityIncreaseUs;
private final long maxDurationForQualityDecreaseUs;
private final long minDurationToRetainAfterDiscardUs;
private final float bandwidthFraction;
private final float bufferedFractionToLiveEdgeForQualityIncrease;
private int selectedIndex;
private int reason;
private ClassAdaptiveTrackSelection(TrackGroup group,
int[] tracks,
BandwidthMeter bandwidthMeter,
int maxInitialBitrate,
long minDurationForQualityIncreaseMs,
long maxDurationForQualityDecreaseMs,
long minDurationToRetainAfterDiscardMs,
float bandwidthFraction,
float bufferedFractionToLiveEdgeForQualityIncrease) {
super(group, tracks);
this.bandwidthMeter = bandwidthMeter;
this.maxInitialBitrate = maxInitialBitrate;
this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L;
this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
this.bandwidthFraction = bandwidthFraction;
this.bufferedFractionToLiveEdgeForQualityIncrease = bufferedFractionToLiveEdgeForQualityIncrease;
selectedIndex = determineIdealSelectedIndex(Long.MIN_VALUE);
reason = C.SELECTION_REASON_INITIAL;
}
#Override
public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) {
long nowMs = SystemClock.elapsedRealtime();
// Stash the current selection, then make a new one.
int currentSelectedIndex = selectedIndex;
selectedIndex = determineIdealSelectedIndex(nowMs);
if (selectedIndex == currentSelectedIndex) {
return;
}
if (!isBlacklisted(currentSelectedIndex, nowMs)) {
// Revert back to the current selection if conditions are not suitable for switching.
Format currentFormat = getFormat(currentSelectedIndex);
Format selectedFormat = getFormat(selectedIndex);
if (selectedFormat.bitrate > currentFormat.bitrate
&& bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
// The selected track is a higher quality, but we have insufficient buffer to safely switch
// up. Defer switching up for now.
selectedIndex = currentSelectedIndex;
} else if (selectedFormat.bitrate < currentFormat.bitrate
&& bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
// The selected track is a lower quality, but we have sufficient buffer to defer switching
// down for now.
selectedIndex = currentSelectedIndex;
}
}
// If we adapted, update the trigger.
if (selectedIndex != currentSelectedIndex) {
reason = C.SELECTION_REASON_ADAPTIVE;
}
}
#Override
public int getSelectedIndex() {
return selectedIndex;
}
#Override
public int getSelectionReason() {
return reason;
}
#Override
public Object getSelectionData() {
return null;
}
#Override
public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
if (queue.isEmpty()) {
return 0;
}
int queueSize = queue.size();
long bufferedDurationUs = queue.get(queueSize - 1).endTimeUs - playbackPositionUs;
if (bufferedDurationUs < minDurationToRetainAfterDiscardUs) {
return queueSize;
}
int idealSelectedIndex = determineIdealSelectedIndex(SystemClock.elapsedRealtime());
Format idealFormat = getFormat(idealSelectedIndex);
// If the chunks contain video, discard from the first SD chunk beyond
// minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal
// track.
for (int i = 0; i < queueSize; i++) {
MediaChunk chunk = queue.get(i);
Format format = chunk.trackFormat;
long durationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs;
if (durationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs
&& format.bitrate < idealFormat.bitrate
&& format.height != Format.NO_VALUE && format.height < 720
&& format.width != Format.NO_VALUE && format.width < 1280
&& format.height < idealFormat.height) {
return i;
}
}
return queueSize;
}
private int determineIdealSelectedIndex(long nowMs) {
if (sHLSQuality != HLSQuality.Auto) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for " + String.valueOf(sHLSQuality));
for (int i = 0; i < length; i++) {
Format format = getFormat(i);
if (HLSUtil.getQuality(format) == sHLSQuality) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality set to " + String.valueOf(sHLSQuality));
return i;
}
}
}
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for auto quality " + String.valueOf(sHLSQuality));
long bitrateEstimate = bandwidthMeter.getBitrateEstimate();
long effectiveBitrate = bitrateEstimate == BandwidthMeter.NO_ESTIMATE
? maxInitialBitrate : (long) (bitrateEstimate * bandwidthFraction);
int lowestBitrateNonBlacklistedIndex = 0;
for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
Format format = getFormat(i);
if (format.bitrate <= effectiveBitrate && HLSUtil.isQualityPlayable(format)) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality auto quality found " + String.valueOf(sHLSQuality));
return i;
} else {
lowestBitrateNonBlacklistedIndex = i;
}
}
}
return lowestBitrateNonBlacklistedIndex;
}
private long minDurationForQualityIncreaseUs(long availableDurationUs) {
boolean isAvailableDurationTooShort = availableDurationUs != C.TIME_UNSET
&& availableDurationUs <= minDurationForQualityIncreaseUs;
return isAvailableDurationTooShort
? (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease)
: minDurationForQualityIncreaseUs;
}
static void setHLSQuality(HLSQuality HLSQuality) {
sHLSQuality = HLSQuality;
}
}
Hope it helps someone.
You can check out ExoPlayer_TrackSelection from github for changing video quality manually.

Refresh Media source in exoplayer

I am using Exo Player ExtractorMediaSource for playing video in my android app. I am downloading media from server and save in local database and on a specific time Alarm i play this media using ConcatenatingMediaSource in exo player. but first i check that all video file downloaded or not and start player with downloaded media source . and if any video is not downloaded then i want to download it in background at when it downloaded then i want to add this video in my already created playlist
This is sample code
private void playAndUpdateVideo(ArrayList<String> mediaSourc) {
simpleExoPlayerView.setVisibility(View.VISIBLE);
simpleExoPlayerView.setDefaultArtwork(null);
mainHandler = new Handler();
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector( videoTrackSelectionFactory);
dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "com.cloveritservices.hype"), bandwidthMeter);
// 2. Create a default LoadControl
extractorsFactory = new DefaultExtractorsFactory();
LoadControl loadControl = new DefaultLoadControl();
// 3. Create the player
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
player.addListener(this);
//Set media controller
simpleExoPlayerView.setUseController(false);
simpleExoPlayerView.requestFocus();
// Bind the player to the view.
simpleExoPlayerView.setPlayer(player);
MediaSource[] mediaSources = new MediaSource[mediaSourc.size()];
for (int i=0;i<mediaSourc.size();i++)
{
mediaSources[i]= buildMediaSource(Uri.parse(mediaSourc.get(i)));
}
MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0]
: new ConcatenatingMediaSource(mediaSources);
LoopingMediaSource loopingSource = new LoopingMediaSource(mediaSource);
player.prepare(loopingSource);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
boolean isChecked = settings.getBoolean("switch", false);
if (!isChecked)
player.setVolume(0f);
else player.setVolume(2f);
player.setPlayWhenReady(true);
}
And here i am checking for video file that it is downloaded or not
if (CommonUtils.isExternalStorageExistAndWritable()) {
for (int i = 0; i < videoUrl.size(); i++) {
if (!new File(Environment.getExternalStorageDirectory().toString() + Constants.PROFILE_VIDEO_FOLDER + CommonUtils.fileFromUrl(videoUrl.get(i))).exists() && !CommonUtils.currentlyDownloading(context,CommonUtils.fileFromUrl(videoUrl.get(i)))) {
downloadByDownloadManager(videoUrl.get(i), CommonUtils.fileFromUrl(videoUrl.get(i)));
if (flag==Constants.FLAG_PLAY){downloadFlag=true;}
}
}
} else {
Toast.makeText(getApplicationContext(), "SD Card not mounted.Please Mount SD Card", Toast.LENGTH_SHORT).show();
}
if (flag==Constants.FLAG_PLAY && !downloadFlag)
{
playAndUpdateVideo(videoUrl);
}
public void downloadByDownloadManager(String url, String fileName1) {
downloadUrl=url;
fileName=fileName1;
request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("video file");
request.setTitle(fileName);
request.setNotificationVisibility(2);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.setDestinationInExternalPublicDir(Constants.PROFILE_VIDEO_FOLDER, fileName);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
// get download service and enqueue file
}
Please help that how to add missing video file later to playlist if it is not downloaded.
To add new video files to your playlist, you need a new MediaSource implementation which can handle a list of sources to enable resizing. This is fairly simple to achieve, the easiest way to do so is to create a modified implementation of ConcaternatingMediaSource which uses lists instead of arrays to store and iterate over media sources. You then replace the ConcaternatingMediaSource in playAndUpdateVideo with the new implementation using lists. This will allow you to add and remove from your playlist as you wish, you can append new media files when your download complete listener is triggered. Here is the full class for a media source implementation using lists:
public final class DynamicMediaSource implements MediaSource {
private List<MediaSource> mediaSources;
private SparseArray<Timeline> timelines;
private SparseArray<Object> manifests;
private Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
private boolean isRepeatOneAtomic;
private Listener listener;
private DynamicTimeline timeline;
/**
* #param mediaSources The {#link MediaSource}s to concatenate. It is valid for the same
* {#link MediaSource} instance to be present more than once in the array.
*/
public DynamicMediaSource(List<MediaSource> mediaSources) {
this(false, mediaSources);
}
/**
* #param isRepeatOneAtomic Whether the concatenated media source shall be treated as atomic
* (i.e., repeated in its entirety) when repeat mode is set to {#code Player.REPEAT_MODE_ONE}.
* #param mediaSources The {#link MediaSource}s to concatenate. It is valid for the same
* {#link MediaSource} instance to be present more than once in the array.
*/
public DynamicMediaSource(boolean isRepeatOneAtomic, List<MediaSource> mediaSources) {
for (MediaSource mediaSource : mediaSources) {
Assertions.checkNotNull(mediaSource);
}
this.mediaSources = mediaSources;
this.isRepeatOneAtomic = isRepeatOneAtomic;
timelines = new SparseArray<Timeline>();
manifests = new SparseArray<Object>();
sourceIndexByMediaPeriod = new HashMap<>();
duplicateFlags = buildDuplicateFlags(mediaSources);
}
#Override
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
this.listener = listener;
for (int i = 0; i < mediaSources.size(); i++) {
if (!duplicateFlags.get(i)) {
final int index = i;
mediaSources.get(i).prepareSource(player, false, new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}
}
}
#Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
for (int i = 0; i < mediaSources.size(); i++) {
if (!duplicateFlags.get(i)) {
mediaSources.get(i).maybeThrowSourceInfoRefreshError();
}
}
}
#Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
int sourceIndex = timeline.getChildIndexByPeriodIndex(id.periodIndex);
MediaPeriodId periodIdInSource =
new MediaPeriodId(id.periodIndex - timeline.getFirstPeriodIndexByChildIndex(sourceIndex));
MediaPeriod mediaPeriod = mediaSources.get(sourceIndex).createPeriod(periodIdInSource, allocator);
sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
return mediaPeriod;
}
#Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
sourceIndexByMediaPeriod.remove(mediaPeriod);
mediaSources.get(sourceIndex).releasePeriod(mediaPeriod);
}
#Override
public void releaseSource() {
for (int i = 0; i < mediaSources.size(); i++) {
if (!duplicateFlags.get(i)) {
mediaSources.get(i).releaseSource();
}
}
}
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.put(sourceFirstIndex, sourceTimeline);
manifests.put(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.put(i, sourceTimeline);
manifests.put(i, sourceManifest);
}
}
for(int i= 0; i<mediaSources.size(); i++){
if(timelines.get(i) == null){
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(timelines, isRepeatOneAtomic);
listener.onSourceInfoRefreshed(timeline, new ArrayList(asList(manifests)));
}
private static SparseArray<Boolean> buildDuplicateFlags(List<MediaSource> mediaSources) {
SparseArray<Boolean> duplicateFlags = new SparseArray<Boolean>();
IdentityHashMap<MediaSource, Void> sources = new IdentityHashMap<>(mediaSources.size());
for (int i = 0; i < mediaSources.size(); i++) {
MediaSource source = mediaSources.get(i);
if (!sources.containsKey(source)) {
sources.put(source, null);
duplicateFlags.append(i,false);
} else {
duplicateFlags.append(i,true);
}
}
return duplicateFlags;
}
/**
* A {#link Timeline} that is the concatenation of one or more {#link Timeline}s.
*/
public static final class DynamicTimeline extends AbstractConcatenatedTimeline {
private final SparseArray<Timeline> timelines;
private final int[] sourcePeriodOffsets;
private final int[] sourceWindowOffsets;
private final boolean isRepeatOneAtomic;
public DynamicTimeline(SparseArray<Timeline> timelines, boolean isRepeatOneAtomic) {
super(timelines.size());
int[] sourcePeriodOffsets = new int[timelines.size()];
int[] sourceWindowOffsets = new int[timelines.size()];
long periodCount = 0;
int windowCount = 0;
for (int i = 0; i < timelines.size(); i++) {
Timeline timeline = timelines.get(i);
periodCount += timeline.getPeriodCount();
Assertions.checkState(periodCount <= Integer.MAX_VALUE,
"ConcatenatingMediaSource children contain too many periods");
sourcePeriodOffsets[i] = (int) periodCount;
windowCount += timeline.getWindowCount();
sourceWindowOffsets[i] = windowCount;
}
this.timelines = timelines;
this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets;
this.isRepeatOneAtomic = isRepeatOneAtomic;
}
#Override
public int getWindowCount() {
return sourceWindowOffsets[sourceWindowOffsets.length - 1];
}
#Override
public int getPeriodCount() {
return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
}
#Override
public int getNextWindowIndex(int windowIndex, #Player.RepeatMode int repeatMode) {
if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
return super.getNextWindowIndex(windowIndex, repeatMode);
}
#Override
public int getPreviousWindowIndex(int windowIndex, #Player.RepeatMode int repeatMode) {
if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
return super.getPreviousWindowIndex(windowIndex, repeatMode);
}
#Override
public int getChildIndexByPeriodIndex(int periodIndex) {
return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
}
#Override
protected int getChildIndexByWindowIndex(int windowIndex) {
return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
}
#Override
protected int getChildIndexByChildUid(Object childUid) {
if (!(childUid instanceof Integer)) {
return C.INDEX_UNSET;
}
return (Integer) childUid;
}
#Override
protected Timeline getTimelineByChildIndex(int childIndex) {
return timelines.get(childIndex);
}
#Override
public int getFirstPeriodIndexByChildIndex(int childIndex) {
return childIndex == 0 ? 0 : sourcePeriodOffsets[childIndex - 1];
}
#Override
protected int getFirstWindowIndexByChildIndex(int childIndex) {
return childIndex == 0 ? 0 : sourceWindowOffsets[childIndex - 1];
}
#Override
protected Object getChildUidByChildIndex(int childIndex) {
return childIndex;
}
}
}
To check when the file is downloaded and set a download completed listener, you can use a BroadcastReceiver. A detailed example of how to set up the BroadcastReceiver is provided here.

recyclerview is not updating

I am trying to update my RecyclerView as the number of elements in the data set changes
PlaceHolderFragment
public class PlaceholderFragment extends Fragment {
private static final String ARG_SECTION_NUMBER = "section_number";
public static final String HOME_ADAPTER = "HOME";
public static final String TRENDING_ADAPTER = "TREND";
public static int P_START = -1;
public static int P_END = -1;
public static int T_START = -1;
public static int T_END = -1;
List<Posts> mNewsFeeds = new ArrayList<>();
List<Posts> mTrending = new ArrayList<>();
List<ChatList> mChatList = new ArrayList<>();
List<Online> mOnline = new ArrayList<>();
HomeAdapter homeAdapter;
HomeAdapter TrendAdapter;
ChatListAdapter chatListAdapter;
OnlineListAdapter onlineAdapter;
RecyclerView recyclerViewT1;
RecyclerView recyclerViewT2;
RecyclerView recyclerViewT3;
RecyclerView recyclerViewT4;
private SwipeRefreshLayout swipeRefreshLayoutT1;
private SwipeRefreshLayout swipeRefreshLayoutT2;
private SwipeRefreshLayout swipeRefreshLayoutT3;
private SwipeRefreshLayout swipeRefreshLayoutT4;
private int visibleThreshold = 2;
private int PlastVisibleItem, PtotalItemCount;
private boolean Ploading;
private int TlastVisibleItem, TtotalItemCount;
private boolean Tloading;
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
Bundle args = new Bundle(getArguments());
int TabID = args.getInt(ARG_SECTION_NUMBER);
if (TabID == 1) {
recyclerViewT1 = (RecyclerView) rootView.findViewById(R.id.rv_numbers);
swipeRefreshLayoutT1 = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh);
final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerViewT1.setLayoutManager(layoutManager);
recyclerViewT1.setHasFixedSize(false);
try {
OnlineltPosts();
} catch (JSONException e) {
e.printStackTrace();
}
recyclerViewT1.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
PtotalItemCount = layoutManager.getItemCount();
PlastVisibleItem = layoutManager.findLastVisibleItemPosition();
if (!Ploading && PtotalItemCount <= (PlastVisibleItem + visibleThreshold)) {
Ploading = true;
OnlineltPosts();
Log.e("coool","yeeeeeeeeeeeeee");
}
}
});
} else if (TabID == 2) {
} else if (TabID == 3) {
} else {
}
return rootView;
}
.........
Onlineltpost
void OnlineltPosts() {
String url = ApiUtil.getltPostUrl(P_END);
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONArray arr = new JSONArray(response);
for (int i = 0; i < arr.length(); i++) {
mNewsFeeds.add(postHelper(arr, i));
}
if (recyclerViewT1.getAdapter() == null) {
homeAdapter = new HomeAdapter(mNewsFeeds, mNewsFeeds.size() + 1, HOME_ADAPTER);
recyclerViewT1.setAdapter(homeAdapter);
}else
homeAdapter.notifyDataSetChanged();
// getActivity().runOnUiThread(new Runnable() {
// public void run() {
// homeAdapter.notifyDataSetChanged();
// }
// });
// Log.e("coool", String.valueOf(mNewsFeeds.size()));
} catch (JSONException | MalformedURLException | URISyntaxException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
RequestManager.getInstance(getContext()).addToRequestQueue(stringRequest);
}
postHelper
public static Posts postHelper(JSONArray arr, int i) throws JSONException, URISyntaxException, MalformedURLException {
JSONObject obj = new JSONObject(String.valueOf(arr.get(i)));
int id = obj.getInt("post_id");
// SharedPreferences pref = getContext().getSharedPreferences(Preferences.POST, 0);
// SharedPreferences.Editor editor = pref.edit();
//
// int start = pref.getInt(Preferences.POST_LATEST_START, -1);
// int end = pref.getInt(Preferences.POST_LATEST_END, -1);
//
// if (start == -1 || id > start)
// editor.putInt(Preferences.POST_LATEST_START, id);
//
// if (end == -1 || id < end)
// editor.putInt(Preferences.POST_LATEST_END, id);
//
// editor.putString(Preferences.POST_PREFEX + id, obj.toString());
//
// editor.apply();
if (P_START == -1 || id > P_START)
P_START = id;
if (P_END == -1 || id < P_END)
P_END = id;
int user_id = obj.getInt("user_id");
String user_name = (String) obj.get("user_name");
String pic = (String) obj.get("user_picture");
URI uri = new URI(pic);
URL user_picture = uri.toURL();
String post_text = (String) obj.get("post_text");
String post_time = (String) obj.get("post_time");
int likes = (int) obj.get("likes");
int dislikes = (int) obj.get("dislikes");
int comments = (int) obj.get("comments");
int isliked = (int) obj.get("isliked");
int isdisliked = (int) obj.get("isdisliked");
int iscommented = (int) obj.get("iscommented");
int isfollow = (int) obj.get("isfollow");
JSONObject abc = obj.getJSONArray("post_data").getJSONObject(0);
String post_image = (String) abc.get("source");
int type = abc.getInt("type");
if (type == 4) {
String youtube = "www.youtube.com/embed/";
int x = post_image.indexOf(youtube);
post_image = post_image.substring(x + youtube.length(), post_image.length());
int x1 = post_image.indexOf("?");
post_image = post_image.substring(0, x1);
post_image = "http://img.youtube.com/vi/" + post_image + "/0.jpg";
}
List<Like> postLikes = new ArrayList<>();
List<Comment> postComments = new ArrayList<>();
JSONArray likeList = obj.getJSONArray("like_all");
for (int j = 0; j < likeList.length(); j++) {
JSONObject obj1 = new JSONObject(String.valueOf(likeList.get(j)));
int Luser_id = obj1.getInt("user_id");
String Luser_name = obj1.getString("user_name");
String Lusername = obj1.getString("username");
String Lpic = obj1.getString("user_picture");
URI uri1 = new URI(Lpic);
URL Luser_picture = uri1.toURL();
Like mlike = new Like(Luser_id, Luser_name, Lusername, Luser_picture);
postLikes.add(mlike);
}
JSONArray CommentList = obj.getJSONArray("post_comment");
for (int j = 0; j < CommentList.length(); j++) {
JSONObject obj1 = new JSONObject(String.valueOf(CommentList.get(j)));
int Lpost_id = obj1.getInt("post_id");
int Lcomment_id = obj1.getInt("comment_id");
String Lusername = obj1.getString("username");
String Lcomment = obj1.getString("comment");
String Lcomment_time = obj1.getString("comment_time");
int Luser_id = obj1.getInt("user_id");
String Luser_name = obj1.getString("user_name");
String Lpic = obj1.getString("user_picture");
URI uri1 = new URI(Lpic);
URL Luser_picture = uri1.toURL();
Comment mlike = new
Comment(Lpost_id, Lcomment_id, Luser_id, Luser_name,
Lusername, Lcomment, Lcomment_time, Luser_picture);
postComments.add(mlike);
}
return new Posts(id, user_id, user_name, user_picture,
post_text, post_time, likes, dislikes, comments,
isliked, isdisliked, iscommented, isfollow, post_image, type,
postLikes, postComments);
}
I have tried recyclerViewT1.setHasFixedSize(false); but nothing is changing. The thing I noticed is that the data set is changing successfully but recyclerview is not showing new items.
when i try:
homeAdapter = new HomeAdapter(mNewsFeeds, mNewsFeeds.size() + 1, HOME_ADAPTER);
recyclerViewT1.setAdapter(homeAdapter);
instead of:
homeAdapter.notifyDataSetChanged();
it shows correct number of items but then user is pushed back to top and user needs to scroll again, which I don't want. I just want to update the data set with the new items.
update the getItemCount & your datas before set notifyDataSetChanged
In your adapter:
private int mListSize;
public void setItemCount(int listsize){
mListSize=listsize;
}
#Override
public int getItemCount() {
return mListSize;
}
before notifydataset changed:
if (recyclerViewT1.getAdapter() == null) {
homeAdapter = new HomeAdapter(mNewsFeeds, mNewsFeeds.size() + 1, HOME_ADAPTER);
recyclerViewT1.setAdapter(homeAdapter);
}else{
homeAdapter.setData(mNewsFeeds, mNewsFeeds.size() + 1, HOME_ADAPTER);
homeAdapter.setItemCount(yourlist.size());
homeAdapter.notifyDataSetChanged();
}
Try this answer also
https://stackoverflow.com/a/40220412/3879847
You should always provide new mNewsFeeds to HomeAdapter and call homeAdapter.notifyDataSetChanged(); to change data
try {
JSONArray arr = new JSONArray(trending);
for (int i = 0; i < arr.length(); i++) {
mNewsFeeds.add(postHelper(arr, i));
}
if (recyclerViewT1.getAdapter() == null) {
homeAdapter = new HomeAdapter(mNewsFeeds, mNewsFeeds.size() + 1, HOME_ADAPTER);
recyclerViewT1.setAdapter(homeAdapter);
}else {
homeAdapter.setNewsFeeds(mNewsFeeds)
}
homeAdapter.notifyDataSetChanged();
// getActivity().runOnUiThread(new Runnable() {
// public void run() {
// homeAdapter.notifyDataSetChanged();
// }
// });
// Log.e("coool", String.valueOf(mNewsFeeds.size()));
} catch (JSONException | MalformedURLException | URISyntaxException e) {
e.printStackTrace();
}

ArrayIndexOutOfBoundsException when parceling set of enums

I can't see the error, I'm having this problem for a long time already... My parcelable class crashes if it is recreated, but I can't find the problem...
I checked the order of writing/reading data.
I checked the functions I use (direct reading/writing vs my custum null save functions)
I marked the line in the first code block that created following exception: java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
SHORT CODE
private Set<ContactType> mMatchesDataLoaded = new HashSet<ContactType>();
saving the set
dest.writeInt(mMatchesDataLoaded.size());
Iterator<ContactType> it = mMatchesDataLoaded.iterator();
while (it.hasNext())
dest.writeInt(it.next().ordinal());
reading the set
int count = source.readInt();
for (int i = 0; i < count; i++)
// ---------------------------------------------------------------------------
// next line produces EXCEPTION!!! java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
// ---------------------------------------------------------------------------
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]);
FULL CODE
I can't see any problems or rather where the problem is...
public class ContactPhone implements Parcelable
{
public static enum ContactType
{
WhatsApp,
Viber,
GooglePlus,
Twitter,
Instagram
}
private boolean mIsUserProfile = false;
private boolean mHasImage;
private int mId;
private long mRawId;
private String mName = null;
private List<PhoneNumber> mNumbers = new ArrayList<PhoneNumber>();
private DBPhoneContact mDBContact = null;
private Set<ContactType> mMatchesDataLoaded = new HashSet<ContactType>();
private HashMap<ContactType, List<BaseMatchContact>> mMatchesData = new HashMap<ContactType, List<BaseMatchContact>>();
// ----------------------
// Parcelable
// ----------------------
#Override
public void writeToParcel(Parcel dest, int flags)
{
ParcelBundleUtils.writeBoolean(dest, mIsUserProfile);
ParcelBundleUtils.writeBoolean(dest, mHasImage);
ParcelBundleUtils.writeIntegerNullSafe(dest, mId);
ParcelBundleUtils.writeLongNullSafe(dest, mRawId);
ParcelBundleUtils.writeStringNullSafe(dest, mName);
dest.writeList(mNumbers);
ParcelBundleUtils.writeLongNullSafe(dest, mDBContact != null ? mDBContact.getId() : null);
// save set
dest.writeInt(mMatchesDataLoaded.size());
Iterator<ContactType> it = mMatchesDataLoaded.iterator();
while (it.hasNext())
dest.writeInt(it.next().ordinal());
// save HashMap
dest.writeInt(mMatchesData.size());
for (Map.Entry<ContactType, List<BaseMatchContact>> entry : mMatchesData.entrySet())
{
dest.writeInt(entry.getKey().ordinal());
dest.writeInt(entry.getValue().size());
for (int i = 0; i < entry.getValue().size(); i++)
dest.writeParcelable(entry.getValue().get(i), 0);
}
}
public void readFromParcel(Parcel source)
{
mIsUserProfile = ParcelBundleUtils.readBoolean(source);
mHasImage = ParcelBundleUtils.readBoolean(source);
mId = ParcelBundleUtils.readIntegerNullSafe(source);
mRawId = ParcelBundleUtils.readLongNullSafe(source);
mName = ParcelBundleUtils.readStringNullSafe(source);
source.readList(mNumbers, PhoneNumber.class.getClassLoader());
Long id = ParcelBundleUtils.readLongNullSafe(source);
mDBContact = null;
if (id != null)
mDBContact = MainApp.getDS().getDBPhoneContactDao().load(id);
// read set
int count = source.readInt();
for (int i = 0; i < count; i++)
// ---------------------------------------------------------------------------
// next line produces EXCEPTION!!! java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
// ---------------------------------------------------------------------------
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]);
// read HashMap
count = source.readInt();
for (int i = 0; i < count; i++)
{
ContactType type = ContactType.values()[source.readInt()];
Class<?> clazz = BaseDef.getMatchClass(type);
// L.d(this, "Classloader: " + clazz.getName() + " type: " + type.name());
int size = source.readInt();
List<BaseMatchContact> list = new ArrayList<BaseMatchContact>();
for (int j = 0; j < size; j++)
list.add((BaseMatchContact) source.readParcelable(clazz.getClassLoader()));
mMatchesData.put(type, list);
}
}
}
The PhoneNumber class implements parcelable and is quite simple and read/writes like following:
#Override
public void writeToParcel(Parcel dest, int flags)
{
ParcelBundleUtils.writeStringNullSafe(dest, mName);
ParcelBundleUtils.writeStringNullSafe(dest, mNormNumber);
ParcelBundleUtils.writeStringNullSafe(dest, mNumber);
}
public void readFromParcel(Parcel source)
{
mName = ParcelBundleUtils.readStringNullSafe(source);
mNormNumber = ParcelBundleUtils.readStringNullSafe(source);
mNumber = ParcelBundleUtils.readStringNullSafe(source);
}
And here are my helper functions:
public static void writeBoolean(Parcel p, boolean b)
{
p.writeByte((byte) (b ? 1 : 0));
}
public static boolean readBoolean(Parcel p)
{
return p.readByte() == 1;
}
public static void writeStringNullSafe(Parcel p, String s)
{
p.writeByte((byte) (s != null ? 1 : 0));
if (s != null)
p.writeString(s);
}
public static void writeIntegerNullSafe(Parcel p, Integer i)
{
p.writeByte((byte) (i != null ? 1 : 0));
if (i != null)
p.writeInt(i);
}
public static void writeLongNullSafe(Parcel p, Long l)
{
p.writeByte((byte) (l != null ? 1 : 0));
if (l != null)
p.writeLong(l);
}
public static void writeDoubleNullSafe(Parcel p, Double d)
{
p.writeByte((byte) (d != null ? 1 : 0));
if (d != null)
p.writeDouble(d);
}
public static void writeParcelableNullSafe(Parcel p, Parcelable d, int flags)
{
p.writeByte((byte) (d != null ? 1 : 0));
if (d != null)
p.writeParcelable(d, flags);
}
public static String readStringNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readString() : null;
}
public static Integer readIntegerNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readInt() : null;
}
public static Long readLongNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readLong() : null;
}
public static Double readDoubleNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readDouble() : null;
}
#SuppressWarnings("unchecked")
public static <T extends Parcelable> T readParcelableNullSafe(Parcel p, ClassLoader classLoader)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? (T) p.readParcelable(classLoader) : null;
}
int count = source.readInt(); // index is raised + 1
for (int i = 0; i < count; i++)
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]); // index is raised by 1, starting with 1!
you loop from 0 to 4, but source.readInt() was already called once, so you called it 5 times in total.
ContactType contains 5 values, from index 0 to index 4. You are trying to access the index 5, which does not exist.
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]);
source.readInt() gives you a 5, try to figure out, with debug, why does it contain this value.
My guess is that writeToParcel writes this 5, try to inspect mMatchesDataLoaded which maybe contains some additional unwanted data.

Categories

Resources