I can display all the books from FireBase Database, but once I use pagination to load 10 books by 10, I have nothing displayed on my screen and I have no error in my Logcut.
Please help me to understand where I'm wrong in my code.
private boolean isLoading;
int mPageEndOffset = 0;
int mPageLimit = 10;
//OnCreate
listAdapter = new FeedListAdapter(getActivity(), feedItems);
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(myClickListener);
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int i) {
}
#Override
public void onScroll(AbsListView absListView, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (mPageEndOffset != 0) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if ((lastInScreen == totalItemCount) && !(isLoading)) {
loadData();
}
}
}
});
//Load More Data from FireBase Database
private void loadData() {
ref.orderByChild("date_creation").limitToLast(mPageLimit).startAt(mPageEndOffset).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
isLoading = true;
for (DataSnapshot dataSnap : dataSnapshot.getChildren()) {
Book valueBook = dataSnap.getValue(Book.class);
keyId = dataSnap.getKey();
String titreLivreToDisplay = valueBook.getNom_livre();
String descLivreToDisplay = valueBook.getDesc_livre();
String prixLivreToDisplay = valueBook.getPrix_livre();
String timeToDisplay = valueBook.getDate_creation();
String filePathToDiplay = valueBook.getChemin_image();
String villeToDisplay = valueBook.getVille_livre();
String typeAnnToDisplat = valueBook.getType_annonce_selected();
String catAnnToDisplay = valueBook.getCat_selected();
}
valueBook.setNom_livre(titreLivreToDisplay);
valueBook.setDesc_livre(descLivreToDisplay);
valueBook.setPrix_livre(prixLivreToDisplay);
valueBook.setDate_creation(timeToDisplay);
valueBook.setChemin_image(filePathToDiplay);
valueBook.setVille_livre(villeToDisplay);
valueBook.setType_annonce_selected(typeAnnToDisplat);
valueBook.setCat_selected(catAnnToDisplay);
valueBook.setKeyIdNode(keyId);
feedItems.add(valueBook);
}
if (feedItems != null && feedItems.size() != 0) {
Collections.reverse(feedItems);
listAdapter.notifyDataSetChanged();
mPageEndOffset += mPageLimit;
isLoading = false;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
String message = "Server error. Refresh page";
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
});
}
You use wrong startAt value for your date_creation field. startAt value should be formatted as date_creation.(e.g. now you have startAt=5, but date_creation=1499705022735 and they can not be compared. You should have startAt=1499705022735).
Try to use something like:
private long offset = 0;
private int limit = 10;
private boolean isLastPage = false;
if (!isLastPage) loadData(offset, limit);
//Condition for loaded data
if (books.size() > 0) {
if (books.size() < limit - 1) isLastPage = true;
else {
offset = books.get(books.size() - 1).getDate_creation();
books = books.subList(0, books.size() - 1);
}
} else isLastPage = true;
//for your firebase reference
ref.orderByChild("createdAt").startAt(offset).limitToFirst(limit);
Keep the value of the field date_creation of the last loaded list item, this will be your field startAt for the next request.
It does not look very nice, but this is the only possible solution for such cases in Firebase and it works.
Related
I have working on mention edit text in below code i am extracting text after typing # but this code in working in some device and not in some device.
I have uploaded both video of working and not working in which showing also that which text is getting in text-watcher in red colour in autocomplete list.
This is working video
This is not working video
Any one help me for getting out this problem .This code is working in some devices but in some devices.
public class SocialMentionAutoComplete extends AppCompatMultiAutoCompleteTextView {
UserSearchChatAdapter userSearchChatAdapter;
ArrayMap<String, UserModel> map = new ArrayMap<>();
String formattedOfString = "#%s ";
Context context;
public SocialMentionAutoComplete(Context context) {
super(context);
initializeComponents(context);
}
public SocialMentionAutoComplete(Context context, AttributeSet attrs) {
super(context, attrs);
initializeComponents(context);
}
public SocialMentionAutoComplete(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeComponents(context);
}
private void initializeComponents(Context context) {
this.context = context;
addTextChangedListener(textWatcher);
setOnItemClickListener(onItemSelectedListener);
setTokenizer(new SpaceTokenizer());
}
AdapterView.OnItemClickListener onItemSelectedListener = new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
UserModel mentionPerson = (UserModel) adapterView.getItemAtPosition(i);
map.put("#" + mentionPerson.username, mentionPerson);
}
};
/***
*This function returns the contents of the AppCompatMultiAutoCompleteTextView into my desired Format
*You can write your own function according to your needs
**/
public String getProcessedString() {
String s = getText().toString();
for (Map.Entry<String, UserModel> stringMentionPersonEntry : map.entrySet()) {
s = s.replace(stringMentionPersonEntry.getKey(), stringMentionPersonEntry.getValue().getFormattedValue());
}
return s;
}
/**
* This function will process the incoming text into mention format
* You have to implement the processing logic
*/
public void setMentioningText(String text) {
map.clear();
Pattern p = Pattern.compile("\\[([^]]+)]\\(([^ )]+)\\)");
Matcher m = p.matcher(text);
String finalDesc = text;
while (m.find()) {
UserModel mentionPerson = new UserModel();
String name = m.group(1);
String username = m.group(2);
//Processing Logic
finalDesc = finalDesc.replace("#[" + name + "](" + username + ")", "#" + username);
mentionPerson.name = name;
mentionPerson.username = username;
map.put("#" + username, mentionPerson);
}
int textColor = ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null);
Spannable spannable = new SpannableString(finalDesc);
for (Map.Entry<String, UserModel> stringMentionPersonEntry : map.entrySet()) {
int startIndex = finalDesc.indexOf(stringMentionPersonEntry.getKey());
int endIndex = startIndex + stringMentionPersonEntry.getKey().length();
spannable.setSpan(new ForegroundColorSpan(textColor), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
setText(spannable);
}
TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence s, int start, int lengthBefore, int lengthAfter) {
try {
if (!s.toString().isEmpty() && s.length() > 1) {//start < s.length()
String name;
if (lengthAfter > lengthBefore) {
name = s.toString().substring(0, start + 1);
} else {
name = s.toString().substring(0, start);
}
if (name.contains("\n")) {
name = name.replaceAll("\n", "");
}
s = name;// this is main passing
int lastTokenIndex = name.lastIndexOf(" #");
int lastIndexOfSpace = name.lastIndexOf(" ");
int nextIndexOfSpace = name.indexOf(" ", start);
if (lastIndexOfSpace > 0 && lastTokenIndex < lastIndexOfSpace) {
String afterString = s.toString().substring(lastIndexOfSpace, s.length());
if (afterString.startsWith(" ") && !afterString.startsWith(" \n")) return;
}
if (lastTokenIndex < 0) {
if (!name.isEmpty() && name.length() >= 1 && name.startsWith("#")) {
lastTokenIndex = 1;
} else
return;
}
int tokenEnd = lastIndexOfSpace;
if (lastIndexOfSpace <= lastTokenIndex) {
tokenEnd = name.length();
if (nextIndexOfSpace != -1 && nextIndexOfSpace < tokenEnd) {
tokenEnd = nextIndexOfSpace;
}
}
if (lastTokenIndex >= 0) {
name = s.toString().substring(lastTokenIndex, tokenEnd).trim();
Pattern pattern = Pattern.compile("^(.+)\\s.+");
Matcher matcher = pattern.matcher(name);
if (!matcher.find()) {
// name = name.replace("#", "").trim();
if (name.equals("#")) {
// getPollsDetail("#");
} else if (!name.isEmpty()) {
getUsers(name);
}
}
}
} else if (!s.toString().isEmpty() && s.length() == 1 && s.toString().equals("#")) {
{
// getPollsDetail("#");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void afterTextChanged(Editable editable) {
}
};
/*
*This function returns results from the web server according to the user name
* I have used Retrofit for Api Communications
* */
public void getUsers(String name) {
AndroidNetworking.cancel("SearchUser");
String url = WebServiceUrl.SEARCH_USER + name.replace("#", "");
new WebTask().nameSearchAPI(context, url, new WebCompleteTaskNew() {
#Override
public void onComplete(String response, int taskcode, String callUrl) {
try {
JSONObject baseResponse = new JSONObject(response);
JSONObject jsonValue = baseResponse.getJSONObject("value");
if (taskcode == RequestCode.CODE_SEARCH_PEOPLE) {
List<UserModel> peopleArrayList = new Gson().fromJson(jsonValue.getJSONArray("data").toString(), new TypeToken<List<UserModel>>() {
}.getType());
userSearchChatAdapter = new UserSearchChatAdapter(getContext(), peopleArrayList, name);
SocialMentionAutoComplete.this.setAdapter(userSearchChatAdapter);
System.out.println("URLL::" + url + "::CLLLLL::" + callUrl);
if (callUrl.equals(url)) {
showDropDown();
}
// SocialMentionAutoComplete.this.showDropDown();
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onError(String message, int taskcode) {
}
});
}
public class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
// Returns colored text for selected token
SpannableString sp = new SpannableString(String.format(formattedOfString, text));
int textColor = ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null);
sp.setSpan(new ForegroundColorSpan(textColor), 0, text.length() + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return sp;
}
}
}
}
I have an activity where credit/debit card details is added. Currently my code allows to add date and month in MM/YY format. But i need year in 4 digits format MM/YYYY. Adding the '/' divider is set after 2nd digit. So if i increase my total digits count, i get the divider after every 2nd digit. How to fix this?
Carddetails.java:
public class CardDetailsActivity extends AppCompatActivity implements TextWatcher {
private static final int CARD_NUMBER_TOTAL_SYMBOLS = 19; // size of pattern 0000-0000-0000-0000
private static final int CARD_NUMBER_TOTAL_DIGITS = 16; // max numbers of digits in pattern: 0000 x walknew2
private static final int CARD_NUMBER_DIVIDER_MODULO = 5; // means divider position is every 5th symbol beginning with walknew4
private static final int CARD_NUMBER_DIVIDER_POSITION = CARD_NUMBER_DIVIDER_MODULO - 1; // means divider position is every 4th symbol beginning with 0
private static final char CARD_NUMBER_DIVIDER = '-';
#BindView(R.id.cardno)
EditText cardno;
#BindView(R.id.cardDateEditText)
EditText cardDateEditText;
#BindView(R.id.cardCVCEditText)
EditText cardCVCEditText;
SharedPreferences pref;
SharedPreferences.Editor editor;
private static final int CARD_DATE_TOTAL_SYMBOLS = 7; // size of pattern MM/YY
private static final int CARD_DATE_TOTAL_DIGITS = 6; // max numbers of digits in pattern: MM + YY
private static final int CARD_DATE_DIVIDER_MODULO = 3; // means divider position is every 3rd symbol beginning with walknew4
private static final int CARD_DATE_DIVIDER_POSITION = CARD_DATE_DIVIDER_MODULO - 1; // means divider position is every 2nd symbol beginning with 0
private static final char CARD_DATE_DIVIDER = '/';
private static final int CARD_CVC_TOTAL_SYMBOLS = 3;
String card_date, card_year, card_month, card_cvv, card_num, user_id, card_type = "debitcard";
#BindView(R.id.btn_add_card)
Button btn_add_card;
#BindView(R.id.btn_back)
Button btn_back;
Context context;
ProgressDialog progressDialog;
ApiService apiService;
String driverid, id, usertype, c_id, booking_id;
EditText card_numm, card_yearm, card_cvvm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card_details);
ButterKnife.bind(this);
context = CardDetailsActivity.this;
Log.e("TAG", "Inside Card Details Activity:");
apiService = RetrofitSingleton.getApiService();
usertype = PrefConnect.readString(CardDetailsActivity.this, PrefConnect.USERTYPE, "");
pref = getApplicationContext().getSharedPreferences("MyPref", 0);
editor = pref.edit();
id = pref.getString("driver_id", "");
Log.e("driver_id", id);
try {
if (getIntent() != null) {
driverid = getIntent().getStringExtra("Driverid");
booking_id = getIntent().getStringExtra("bookingid");
}
} catch (Exception e) {
e.printStackTrace();
Log.e("paymentact", e.getMessage());
}
cardno.getText().toString().trim();
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
cardno.addTextChangedListener(this);
cardDateEditText.addTextChangedListener(this);
card_numm = (EditText) findViewById(R.id.cardno);
card_yearm = (EditText) findViewById(R.id.cardDateEditText);
card_cvvm = (EditText) findViewById(R.id.cardCVCEditText);
}
#OnClick({R.id.btn_add_card, R.id.btn_back})
public void OnClick(View view) {
switch (view.getId()) {
case R.id.btn_back:
finish();
break;
case R.id.btn_add_card:
View view1 = getCurrentFocus();
Log.e("TAG", "Inside Card Details Activity Button Add Card ");
Validation();
break;
}
}
private boolean isInputCorrect(Editable s, int size, int dividerPosition, char divider) {
boolean isCorrect = s.length() <= size;
for (int i = 0; i < s.length(); i++) {
if (i > 0 && (i + 1) % dividerPosition == 0) {
isCorrect &= divider == s.charAt(i);
} else {
isCorrect &= Character.isDigit(s.charAt(i));
}
}
return isCorrect;
}
private String concatString(char[] digits, int dividerPosition, char divider) {
final StringBuilder formatted = new StringBuilder();
for (int i = 0; i < digits.length; i++) {
if (digits[i] != 0) {
formatted.append(digits[i]);
if ((i > 0) && (i < (digits.length - 1)) && (((i + 1) % dividerPosition) == 0)) {
formatted.append(divider);
}
}
}
return formatted.toString();
}
private char[] getDigitArray(final Editable s, final int size) {
char[] digits = new char[size];
int index = 0;
for (int i = 0; i < s.length() && index < size; i++) {
char current = s.charAt(i);
if (Character.isDigit(current)) {
digits[index] = current;
index++;
}
}
return digits;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable s) {
card_num = String.valueOf(s);
Log.i("TAG", "Card Date:" + s);
Log.i("TAG", "Card Date :" + cardDateEditText.getEditableText());
if (s == cardno.getEditableText()) {
if (!isInputCorrect(s, CARD_NUMBER_TOTAL_SYMBOLS, CARD_NUMBER_DIVIDER_MODULO, CARD_NUMBER_DIVIDER)) {
s.replace(0, s.length(), concatString(getDigitArray(s, CARD_NUMBER_TOTAL_DIGITS), CARD_NUMBER_DIVIDER_POSITION, CARD_NUMBER_DIVIDER));
}
// DO STH
} else if (s == cardDateEditText.getEditableText()) {
if (!isInputCorrect(s, CARD_DATE_TOTAL_SYMBOLS, CARD_DATE_DIVIDER_MODULO, CARD_DATE_DIVIDER)) {
s.replace(0, s.length(), concatString(getDigitArray(s, CARD_DATE_TOTAL_DIGITS), CARD_DATE_DIVIDER_POSITION, CARD_DATE_DIVIDER));
} else if (s == cardCVCEditText.getEditableText()) {
if (s.length() > CARD_CVC_TOTAL_SYMBOLS) {
s.delete(CARD_CVC_TOTAL_SYMBOLS, s.length());
}
}
}
}
}
As by my understanding, your date divider must always be at position 2
private static final int CARD_DATE_DIVIDER_POSITION = CARD_DATE_DIVIDER_MODULO - 1; // means divider position is every 2nd symbol beginning with 0
with the above declaration, before appending the date divider, I should test this way ( you can change your isInputCorrect method this way):
private boolean isInputCorrect(Editable s, int size, int dividerPosition, char divider) {
boolean isCorrect = s.length() <= size;
for (int i = 0; i < s.length(); i++) {
if (i > 0 && (i + 1) % dividerPosition == 0) {
if (divider=='/') {
if (i==2) {
isCorrect &= divider == s.charAt(i);
}
}
} else {
isCorrect &= Character.isDigit(s.charAt(i));
}
}
return isCorrect;
}
And then after the text has changed can be modified a bit to become:
#Override
public void afterTextChanged(Editable s) {
card_num = String.valueOf(s);
String ss=cardDateEditText.getText().toString();
Log.i("TAG", "Card Date:" + s);
Log.i("TAG", "Card Date :" + ss);
if (s == cardno.getEditableText()) {
if (!isInputCorrect(s, CARD_NUMBER_TOTAL_SYMBOLS, CARD_NUMBER_DIVIDER_MODULO, CARD_NUMBER_DIVIDER)) {
s.replace(0, s.length(), concatString(getDigitArray(s, CARD_NUMBER_TOTAL_DIGITS), CARD_NUMBER_DIVIDER_POSITION, CARD_NUMBER_DIVIDER));
}
} else if (s == cardDateEditText.getEditableText()) {
if (s.length()<=CARD_DATE_TOTAL_SYMBOLS)
{
if (!isInputCorrect(s, CARD_DATE_TOTAL_SYMBOLS, CARD_DATE_DIVIDER_MODULO, CARD_DATE_DIVIDER)) {
Log.e("TAG",s+"");
s.replace(0, s.length(), concatString(getDigitArray(s, CARD_DATE_TOTAL_DIGITS), CARD_DATE_DIVIDER_POSITION, CARD_DATE_DIVIDER));
}
}else
{
s.delete(CARD_DATE_TOTAL_SYMBOLS,s.length());
}
} else if (s == cardCVCEditText.getEditableText()) {
if (s.length() > CARD_CVC_TOTAL_SYMBOLS) {
s.delete(CARD_CVC_TOTAL_SYMBOLS, s.length());
}
}
}
So you test also the length of the editable text.
The date divider must be included only once and just after the month represented by two first characters. That means the last digit must be at 2nd position index 1, at that instant i==1. So no other divider will be appended. I should have to test if the divider set is for date, to know I'm dealing with the date and then test if the index is not greater or equals to 2.
That's how I could handle that. I hope this will help.
I am trying to add a feature like or dislike to my post(Question). But it is not updating the like button image.
public class QuestionDetailActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "QuestionDetailActivity";
private Context mContext;
private TextView questionTitleTv;
private RadioGroup radioLL;
private EditText answerEt;
private LinearLayout checkboxLL;
private LinearLayout commentLL;
private FirebaseFirestore db = null;
private String questionID;
private String userID;
private TextView likeCountTv,favouriteCountTv;
private boolean isLikedByUser = false,isFavouriteByUser = false;
private DocumentReference questionRef;
private QuestionBO questionBO;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_question_detail);
mContext = QuestionDetailActivity.this;
db = FirebaseFirestore.getInstance();
userID = "V5BUeBFYR8WKOQUiBFxKrfMHK8Y2";
questionTitleTv = findViewById(R.id.tv_question_title);
radioLL = findViewById(R.id.ll_radio);
answerEt = findViewById(R.id.et_answer);
checkboxLL = findViewById(R.id.ll_checkbox);
commentLL = findViewById(R.id.ll_comments);
likeCountTv = findViewById(R.id.tv_like_count);
favouriteCountTv = findViewById(R.id.tv_favourite_count);
likeCountTv.setOnClickListener(this);
favouriteCountTv.setOnClickListener(this);
if (getIntent() != null) {
questionID = getIntent().getExtras().getString("keyQuestionID", "");
}
questionRef = db.collection("questionCollection").document(questionID);
if (!questionID.isEmpty()) {
getQuestionDetail();
///showComments();
updateFavouriteAndLikeIcons();
}
}
private void updateFavouriteAndLikeIcons() {
questionRef.collection("favouriteCollection").document(userID).get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if(documentSnapshot.exists()){
isFavouriteByUser = true;
// and make it blue
Drawable image = getResources().getDrawable( R.drawable.icn_fav_yes );
int h = image.getIntrinsicHeight();
int w = image.getIntrinsicWidth();
image.setBounds( 0, 0, w, h );
favouriteCountTv.setCompoundDrawables( image, null, null, null );
}
}
});
questionRef.collection("likeCollection").document(userID).get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if(documentSnapshot.exists()){
boolean isLiked = documentSnapshot.getBoolean(userID);
if(isLiked){
isLikedByUser = true;
// and make it blue
Drawable image = getResources().getDrawable( R.drawable.icn_like_yes );
int h = image.getIntrinsicHeight();
int w = image.getIntrinsicWidth();
image.setBounds( 0, 0, w, h );
likeCountTv.setCompoundDrawables( image, null, null, null );
}
else{//dislike
}
}
}
});
}
private void showComments() {
db.collection("commentCollection").whereEqualTo("questionID", questionID)
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
if (queryDocumentSnapshots.isEmpty()) {
Log.d(TAG, "onSuccess: LIST EMPTY");
return;
} else {
// Convert the whole Query Snapshot to a list
// of objects directly! No need to fetch each
// document.
List<CommentBO> commentList = queryDocumentSnapshots.toObjects(CommentBO.class);
if (commentList != null && commentList.size() > 0) {
for (int x = 0; x < commentList.size(); x++) {
try {
LinearLayout.LayoutParams childParam = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
childParam.setMargins(20, 10, 0, 0);
View commentByView = LayoutInflater.from(mContext).inflate(R.layout.item_comment, null);
TextView tvCommenter = (TextView) commentByView.findViewById(R.id.item_tv_commenter_name);
TextView tvCommenterComment = (TextView) commentByView.findViewById(R.id.item_tv_commenter_comment);
TextView tvCommentDate = (TextView) commentByView.findViewById(R.id.item_tv_comment_date);
tvCommenter.setText(commentList.get(x).getCommentedBy());
tvCommenterComment.setText(commentList.get(x).getComment());
///tvCommentDate.setText();
commentLL.addView(commentByView, childParam);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
});
}
private void getQuestionDetail() {
questionRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d(TAG, "DocumentSnapshot data: " + document.getData());
questionBO = document.toObject(QuestionBO.class);
updateView(questionBO);
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
private void updateView(QuestionBO mQuestionBO) {
if (mQuestionBO != null) {
questionTitleTv.setText(mQuestionBO.getTitle());
likeCountTv.setText(mQuestionBO.getTotalLikes() + "");
favouriteCountTv.setText(mQuestionBO.getTotalFavourites() + "");
switch (mQuestionBO.getQuestionType()) {
case CHECKBOX:
checkboxLL.setVisibility(View.VISIBLE);
if (mQuestionBO.getOptions() != null && mQuestionBO.getOptions().size() > 0) {
checkboxLL.removeAllViews();
for (int x = 0; x < mQuestionBO.getOptions().size(); x++) {
final CheckBox subCheckbox = new CheckBox(mContext);
subCheckbox.setText(mQuestionBO.getOptions().get(x));
subCheckbox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
checkboxLL.addView(subCheckbox, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
}
}
break;
case RADIO:
radioLL.setVisibility(View.VISIBLE);
if (mQuestionBO.getOptions() != null && mQuestionBO.getOptions().size() > 0) {
radioLL.removeAllViews();
for (int x = 0; x < mQuestionBO.getOptions().size(); x++) {
final RadioButton subRadio = new RadioButton(mContext);
subRadio.setText(mQuestionBO.getOptions().get(x));
radioLL.addView(subRadio, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
}
}
break;
case DESCRIPTIVE:
answerEt.setVisibility(View.VISIBLE);
break;
}
}
}
private void likeOrDislikeThisQuestion(){
final Task<Boolean> booleanTask = db.runTransaction(new Transaction.Function<Boolean>() {
#Nullable
#Override
public Boolean apply(#NonNull Transaction transaction) throws FirebaseFirestoreException {
DocumentReference likeRef = questionRef.collection("likeCollection")
.document(userID);
Map<String,Object> map = new HashMap<>();
DocumentSnapshot snapshot = transaction.get(likeRef);
if(snapshot.exists()){ // it means this is a favourite question of this user.
if(snapshot.getBoolean(questionID) && isLikedByUser){
isLikedByUser = false;
map.put(questionID,isLikedByUser);
transaction.update(likeRef,map);
int numberOfLikes = questionBO.getTotalLikes();
transaction.update(questionRef,"totalLikes",--numberOfLikes);
likeCountTv.setText(numberOfLikes + "");
return isLikedByUser;
}
else if(!snapshot.getBoolean(questionID) && !isLikedByUser){
isLikedByUser = true;
map.put(questionID,isLikedByUser);
transaction.update(likeRef,map);
int numberOfLikes = questionBO.getTotalLikes();
transaction.update(questionRef,"totalLikes",-++numberOfLikes);
likeCountTv.setText(numberOfLikes + "");
return isLikedByUser;
}
}
else {
isLikedByUser = !isLikedByUser;
map.put(questionID,isLikedByUser);// pass oposite of it..
transaction.set(likeRef,map);
int numberOfLikes = questionBO.getTotalLikes();
if(isLikedByUser)
++numberOfLikes;
else
--numberOfLikes;
transaction.update(questionRef,"totalLikes",numberOfLikes);
likeCountTv.setText(numberOfLikes + "");
return isLikedByUser;
}
return null;
}
});
booleanTask.addOnSuccessListener(new OnSuccessListener<Boolean>() {
#Override
public void onSuccess(Boolean isLiked) {
if(isLiked == null){
}
else if(isLiked){
Drawable image = getResources().getDrawable( R.drawable.icn_like_yes );
int h = image.getIntrinsicHeight();
int w = image.getIntrinsicWidth();
image.setBounds( 0, 0, w, h );
likeCountTv.setCompoundDrawables( image, null, null, null );
}
else{
Drawable image = getResources().getDrawable( R.drawable.icn_like_no );
int h = image.getIntrinsicHeight();
int w = image.getIntrinsicWidth();
image.setBounds( 0, 0, w, h );
likeCountTv.setCompoundDrawables( image, null, null, null );
}
}
});
}
private void favouriteOrDisFavouriteQuestion(){
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.tv_like_count:
likeOrDislikeThisQuestion();
break;
case R.id.tv_favourite_count:
break;
}
}
}
I updated icon using updateFavouriteAndLikeIcons() for the first time. then when user clicks on like button I called likeOrDislikeThisQuestion() . Counter updating but left drawable not updating. and sometimes variable isLikedByUser become false .
If the question is already liked then likeCollection must have that Document. if that document don't exist it means User didn't like or dislike it.
My architecture of db is like
- likeCollection
userID (Document ID)
questionID = true // true for like and false for dislike..
From Firebase docs :
Do not modify application state inside of your transaction functions. Doing so will introduce concurrency issues, because transaction functions can run multiple times and are not guaranteed to run on the UI thread. Instead, pass information you need out of your transaction functions.
This is what happens here, you have to run every application modifications inside a listener on your variable booleanTask. Do not change class variable in the transaction's apply method.
In the addValueEventListener I am getting data for firebase database correctly. I copy that data to a variable called newDay. However, once I am out of the addValueEventListener, the data is gone. How do I make sure the data stays?
mCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
String current_uid = mCurrentUser.getUid();
mUserDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(current_uid).child("week").child(day);
newDay = new ArrayList<Integer>();
mUserDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
newDay.addAll((ArrayList<Integer>)dataSnapshot.getValue());
Log.d("newday", "onDataChange:"+newDay.toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Log.d("after","value " +newDay.toString());
These are the log results:
03-04 00:01:28.475 7961-7961/com.example.fake9.tendee D/newday: onDataChange:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
In after, newDay is strangely empty.
03-04 00:04:54.665 9906-9906/com.example.fake9.tendee D/after: value []
Entire code provided if details are not enough. Many parts are irrelevant it seems:
public class ScheduleActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private Toolbar mToolbar;
private Spinner chooseDay;
private TimePicker startTime;
private TimePicker endTime;
private Button busyBtn;
private Button freeBtn;
String day;
Map<String,ArrayList<Integer>> changeweek;
ArrayList<Integer> test;
ArrayList<Integer> newDay;
private DatabaseReference mUserDatabase;
private FirebaseUser mCurrentUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_schedule);
mToolbar = (Toolbar)findViewById(R.id.schedule_toolbar);
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle("Schedule");
chooseDay = (Spinner)findViewById(R.id.chooseDay_spinner);
chooseDay.setOnItemSelectedListener(this);
List<String> week = new ArrayList<String>();
week.add("Monday");
week.add("Tuesday");
week.add("Wednesday");
week.add("Thursday");
week.add("Friday");
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,week);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
chooseDay.setAdapter(dataAdapter);
startTime = (TimePicker)findViewById(R.id.start_timePicker);
endTime = (TimePicker)findViewById(R.id.end_timePicker);
freeBtn = (Button)findViewById(R.id.free_btn);
freeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int startHour = startTime.getCurrentHour();
int startMin = startTime.getCurrentMinute();
int endHour = endTime.getCurrentHour();
int endMin = endTime.getCurrentMinute();
int startIndex = 0;
int endIndex = 0;
int intervals = 0;
//Log.d("hour", "onClick: "+startHour);
//Log.d("minute", "onClick: "+startMin);
if (startHour < 9 || startHour > 17 || endHour < 9 || endHour > 17) {
Toast.makeText(ScheduleActivity.this, "invalid time", Toast.LENGTH_SHORT).show();
return;
}
if (startHour > endHour) {
Toast.makeText(ScheduleActivity.this, "impossible time", Toast.LENGTH_SHORT).show();
return;
}
startIndex = (startHour - 9) * 2;
if (startMin >= 30) {
startIndex = startIndex + 1;
}
//Log.d("startIndex", "onClick: "+startIndex);
endIndex = (endHour - 9) * 2;
if (endMin >= 30) {
endIndex = endIndex + 1;
}
final int finalstart = startIndex;
final int finalend = endIndex;
//Log.d("endIndex", "onClick: "+endIndex);
mCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
String current_uid = mCurrentUser.getUid();
mUserDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(current_uid).child("week").child(day);
newDay = new ArrayList<Integer>();
mUserDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
newDay.addAll((ArrayList<Integer>)dataSnapshot.getValue());
Log.d("newday", "onDataChange:"+newDay.toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Log.d("after","value " +newDay.toString());
intervals = endIndex - startIndex;
for (int i = startIndex; i < endIndex; i++) {
Log.d("indexes", "onClick: "+i);
}
}
});
}
The line
Log.d("after","valu" + newDay.toString());
is calling before firebase gets result from database.
All you need to do is create a method
public void printResult(){
Log.d("after","valu" + newDay.toString());
}
Now call this method as
newDay = new ArrayList<Integer>();
mUserDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
newDay.addAll((ArrayList<Integer>)dataSnapshot.getValue());
Log.d("newday", "onDataChange:"+newDay.toString());
printResult();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Reason
Firebase has it's own async mechanism like volley library onDataChange function will be call when firebase gets some result from internet and execute the below code as normally in your case below code is your Log.d("after","value"+newDay.toString()); line.
Hope this will help you.
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);
}
}
}