Obserable status - android

Hello I have this program :
That after a input data should show button bellow but obserable doesnt work at all
I have made 3 statuses each for input and if all of them is true I should show button (another status)
How could i display a button after input in all fields. If you need more code please tell me
package com.example.android_lab1;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements LifecycleOwner {
Button btn;
MainActivityViewModel model;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
model = new ViewModelProvider(this).get(MainActivityViewModel.class);
btn = findViewById(R.id.check_btn);
EditText i_imie = findViewById(R.id.input_imie);
EditText i_nazwisko = findViewById(R.id.input_nazwisko);
EditText i_l_ocen = findViewById(R.id.input_l_ocen);
i_imie.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
if (i_imie.getText().toString().isEmpty()) {
Toast.makeText(v.getContext(), "Imie nie moze byc puste", Toast.LENGTH_SHORT).show();
model.setImie_status(false);
}else{
model.setImie_status(true);
}
}
}
});
i_nazwisko.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
if (i_nazwisko.getText().toString().isEmpty()) {
Toast.makeText(v.getContext(), "Nazwisko nie moze byc puste", Toast.LENGTH_SHORT).show();
model.setNazwisko_status(false);
}else{
model.setNazwisko_status(true);
}
}
}
});
i_l_ocen.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
if (i_l_ocen.getText().toString().isEmpty()) {
Toast.makeText(v.getContext(), "Liczba ocen nie moze byc pusta", Toast.LENGTH_SHORT).show();
} else {
int x = Integer.parseInt(i_l_ocen.getText().toString());
if (x < 5 || x > 15) {
model.setL_ocen_status(false);
Toast.makeText(v.getContext(), "Liczba ocen musi byc w przedziale 5 -15", Toast.LENGTH_SHORT).show();
}else{
model.setL_ocen_status(true);
}
}
}
}
});
model.getImie().observe(this, string -> {
i_imie.setText(string);
if(!string.isEmpty()){
model.setImie_status(true);
}
});
model.getNazwisko().observe(this, string -> {
i_nazwisko.setText(string);
if(!string.isEmpty()){
model.setNazwisko_status(true);}
});
model.getLOcen().observe(this, integer -> {
i_l_ocen.setText(integer.toString());
if(integer>=5 && integer<=15){
model.setL_ocen_status(true);
}
});
model.getBtn_status().observe(this, aBoolean -> {
if(aBoolean == false){
btn.setVisibility(View.INVISIBLE);
}else if(aBoolean ==true){
btn.setVisibility(View.VISIBLE);
}
});
btn.setOnClickListener(v -> {
checkData(i_imie, i_nazwisko, i_l_ocen);
});
}
void checkData(EditText e_imie, EditText e_nazwisko, EditText e_l_ocen) {
String imie = e_imie.getText().toString();
String nazwisko = e_nazwisko.getText().toString();
if (imie.isEmpty() || nazwisko.isEmpty() || e_l_ocen.getText().toString().isEmpty()) {
if (imie.isEmpty()) {
e_imie.setError("Imie nie moze byc puste");
}
if (nazwisko.isEmpty()) {
e_nazwisko.setError("Nazwisko nie moze byc puste");
}
if (e_l_ocen.getText().toString().isEmpty()) {
e_l_ocen.setError("Liczba ocen nie moze byc pusta");
}
} else {
if (Integer.parseInt(e_l_ocen.getText().toString()) < 5 || Integer.parseInt(e_l_ocen.getText().toString()) > 15) {
e_l_ocen.setError("Liczba ocen musi byc w zakresie 5-15");
} else {
saveData(imie,nazwisko, Integer.parseInt( e_l_ocen.getText().toString()));
}
}
}
private void saveData(String imie, String nazwisko, Integer l_ocen) {
model.setImie(imie);
model.setNazwisko(nazwisko);
model.setL_ocen(l_ocen);
model.setNazwisko_status(true);
}
}
ViewModel:
package com.example.android_lab1;
import android.view.View;
import android.widget.Button;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class MainActivityViewModel extends ViewModel {
private MutableLiveData<String> imie;
private MutableLiveData<String> nazwisko;
private MutableLiveData<Integer> l_ocen;
private MutableLiveData<Boolean> imie_status, nazwisko_status, l_ocen_status, btn_status;
public MainActivityViewModel() {
imie = new MutableLiveData<>();
nazwisko= new MutableLiveData<>();
l_ocen = new MutableLiveData<>();
imie_status = new MutableLiveData<>(false);
nazwisko_status = new MutableLiveData<>(false);
l_ocen_status = new MutableLiveData<>(false);
btn_status = new MutableLiveData<>();
}
LiveData<String> getImie(){
return imie;
}
LiveData<String> getNazwisko(){
return nazwisko;
}
LiveData<Integer> getLOcen(){
return l_ocen;
}
LiveData<Boolean> getBtn_status(){
if(imie_status.getValue() ==true || nazwisko_status.getValue() == true || l_ocen_status.getValue()==true){
btn_status.setValue(true);
}else{
btn_status.setValue(false);
}
return btn_status;
}
public void setImie(String imie) {
this.imie.setValue(imie);
}
public void setNazwisko(String nazwisko) {
this.nazwisko.setValue(nazwisko);
}
public void setL_ocen(Integer l_ocen) {
this.l_ocen.setValue(l_ocen);
}
public void setImie_status(Boolean btn_status){
this.imie_status.setValue(btn_status);
}
public void setNazwisko_status(Boolean btn_status){
this.nazwisko_status.setValue(btn_status);
}
public void setL_ocen_status(Boolean btn_status){
this.l_ocen_status.setValue(btn_status);
}
public void setBtn_status(Boolean btn_status){
this.btn_status.setValue(btn_status);
}
}

In your view model you should create a method to update btn_status, and call after each status is set:
private void updateBtnStatus(){
btn_status.setValue(imie_status.getValue() || nazwisko_status.getValue() || l_ocen_status.getValue());
}
public void setImie_status(Boolean btn_status){
this.imie_status.setValue(btn_status);
updateBtnStatus();
}
public void setNazwisko_status(Boolean btn_status){
this.nazwisko_status.setValue(btn_status);
updateBtnStatus();
}
public void setL_ocen_status(Boolean btn_status){
this.l_ocen_status.setValue(btn_status);
updateBtnStatus();
}
Also, you should consider to use addTextChangedListener instead of setOnFocusChangeListener to avoid to have to focus another field after filled the last one:
i_l_ocen.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
}
});

Related

Configure ItemTouchHelper in RecyclerView to work for Snackbar being dismissed by another Snackbar being displayed

I have a RecyclerView in my app that contains messages. On swiping right, I've configured it to delete the messages and on swiping left, to archive the messages. Here's the code for the ItemTouchHelper part related to deleting and archiving:
if (direction == ItemTouchHelper.LEFT) {
donorMessagesAdapter.deleteMessages(position);
} else if (direction == ItemTouchHelper.RIGHT) {
donorMessagesAdapter.archiveMessages(position);
}
Here are the deleteMessages() and archiveMessages() functions (they're almost the same):
public void deleteMessages(final int position) {
recentlyDeletedPeer = sortedPeers.get(position);
recentlyDeletedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages Deleted", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View view) {
sortedPeers.add(recentlyDeletedPeerPosition, recentlyDeletedPeer);
notifyItemInserted(recentlyDeletedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().deleteAllReceivedMessagesFromSender(email, recentlyDeletedPeer);
MainActivity.database.sentMessagesDao().deleteAllSentMessagesFromReceiver(email, recentlyDeletedPeer);
}
}
});
snackbar.show();
}
public void archiveMessages(final int position) {
recentlyArchivedPeer = sortedPeers.get(position);
recentlyArchivedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages archived", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View view) {
sortedPeers.add(recentlyArchivedPeerPosition, recentlyArchivedPeer);
notifyItemInserted(recentlyArchivedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().archive(email, recentlyArchivedPeer);
MainActivity.database.sentMessagesDao().archive(email, recentlyArchivedPeer);
}
}
});
snackbar.show();
}
Everything works perfectly well when deleting/archiving one message. Swiping deletes or archives the message after the Snackbar disappears, and when I click undo, the deletion/archival is reversed. Problems arise when deleting/archiving multiple messages.
Problem 1: When I delete/archive multiple messages without waiting for the Snackbar to disappear for each message, all except the first message is deleted/archived. What it makes it stranger for me is that it's not that only the last message is deleted/archived, all except the first are deleted/archived.
Problem 2: When I delete/archive multiple messages without waiting for the Snackbar to disappear for each message, if I press UNDO, that is, on the Snackbar for the last message that was deleted/archived, the app crashes.
By the way,I'm using Room Library to store the messages in a database (referenced by MainActivity.database).
I think the problem is in the addCallback() function for the Snackbar. I'm supposed to do something differently for event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE from event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT, but I can't figure out exactly what I'm supposed to do differently, and what exactly causes the problem.
Here is my entire adapter class for reference (error message lines marked with comment next to them):
package com.example.treeapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
import com.mikhaellopez.circularimageview.CircularImageView;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class DonorMessagesAdapter extends RecyclerView.Adapter<DonorMessagesAdapter.DonorMessagesViewHolder> { // line 24 in error message
private Activity activity;
public String email;
public List<String> sortedPeers;
public static CircularImageView imageView;
private String recentlyDeletedPeer;
private int recentlyDeletedPeerPosition;
private String recentlyArchivedPeer;
private int recentlyArchivedPeerPosition;
public static class DonorMessagesViewHolder extends RecyclerView.ViewHolder {
public Button middleButton;
public TextView nameTextView;
public TextView messageTextView;
DonorMessagesViewHolder(View view) {
super(view);
middleButton = view.findViewById(R.id.middle_button);
imageView = view.findViewById(R.id.image_view);
nameTextView = view.findViewById(R.id.text_view_name);
messageTextView = view.findViewById(R.id.text_view_message);
}
}
DonorMessagesAdapter(Activity activity, String email) {
this.activity = activity;
this.email = email;
this.sortedPeers = getSortedPeers(email);
}
#NonNull
#Override
public DonorMessagesViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.message, parent, false);
return new DonorMessagesViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull DonorMessagesViewHolder holder, int position) {
final String current = sortedPeers.get(position);
// Start of getting last message
String lastMessage;
String lastMessageType;
List<Integer> receivedMessages = MainActivity.database.receivedMessagesDao().getMessagesToReceiverFromSender(email, current, 0);
List<Integer> sentMessages = MainActivity.database.sentMessagesDao().getMessagesBySenderToReceiver(email, current, 0);
if (sentMessages.size() == 0) {
lastMessage = MainActivity.database.receivedMessagesDao().getMessage(receivedMessages.get(receivedMessages.size() - 1)); // line 74 in error message
lastMessageType = "received";
if (MainActivity.database.receivedMessagesDao().getImage(receivedMessages.get(receivedMessages.size() - 1)) == 1) {
lastMessageType = "receivedImage";
}
} else if (receivedMessages.size() == 0) {
lastMessage = MainActivity.database.sentMessagesDao().getMessage(sentMessages.get(sentMessages.size() - 1));
lastMessageType = "sent";
if (MainActivity.database.sentMessagesDao().getImage(sentMessages.get(sentMessages.size() - 1)) == 1) {
lastMessageType = "sentImage";
}
} else {
LocalDateTime lastReceivedMessageTime = LocalDateTime.parse(MainActivity.database.receivedMessagesDao().getTime(receivedMessages.get(receivedMessages.size() - 1)));
LocalDateTime lastSentMessageTime = LocalDateTime.parse(MainActivity.database.sentMessagesDao().getTime(sentMessages.get(sentMessages.size() - 1)));
if (lastReceivedMessageTime.isAfter(lastSentMessageTime)) {
lastMessage = MainActivity.database.receivedMessagesDao().getMessage(receivedMessages.get(receivedMessages.size() - 1));
lastMessageType = "received";
if (MainActivity.database.receivedMessagesDao().getImage(receivedMessages.get(receivedMessages.size() - 1)) == 1) {
lastMessageType = "receivedImage";
}
} else {
lastMessage = MainActivity.database.sentMessagesDao().getMessage(sentMessages.get(sentMessages.size() - 1));
lastMessageType = "sent";
if (MainActivity.database.sentMessagesDao().getImage(sentMessages.get(sentMessages.size() - 1)) == 1) {
lastMessageType = "sentImage";
}
}
}
// End of getting last message
if (MainActivity.database.plantersDao().checkEmail(current) == 1) {
if (MainActivity.database.plantersDao().getImageUrl(current) != null) {
new DownloadImageTask(imageView).execute(MainActivity.database.plantersDao().getImageUrl(current));
}
} else if (MainActivity.database.donorsDao().checkEmail(current) == 1) {
if (MainActivity.database.donorsDao().getImageUrl(current) != null) {
new DownloadImageTask(imageView).execute(MainActivity.database.donorsDao().getImageUrl(current));
}
}
if (MainActivity.database.plantersDao().checkEmail(current) == 1) {
holder.nameTextView.setText(MainActivity.database.plantersDao().getName(current));
} else if (MainActivity.database.donorsDao().checkEmail(current) == 1) {
holder.nameTextView.setText(MainActivity.database.donorsDao().getName(current));
}
if (lastMessageType.equals("received")) {
if (lastMessage.length() > 17) {
holder.messageTextView.setText(lastMessage.substring(0, 16) + "...");
} else {
holder.messageTextView.setText(lastMessage);
}
} else if (lastMessageType.equals("sent")) {
if (lastMessage.length() > 12) {
holder.messageTextView.setText("You: " + lastMessage.substring(0, 11) + "...");
} else {
holder.messageTextView.setText("You: " + lastMessage);
}
} else if (lastMessageType.equals("receivedImage")) {
holder.messageTextView.setText("Image");
} else if (lastMessageType.equals("sentImage")) {
holder.messageTextView.setText("You: " + "Image");
}
holder.middleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(view.getContext(), MessageActivity.class);
intent.putExtra("email", email);
intent.putExtra("peerEmail", current);
intent.putExtra("archived", 0);
view.getContext().startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return sortedPeers.size();
}
public void reload() {
sortedPeers = getSortedPeers(email);
notifyDataSetChanged();
}
private List<String> getSortedPeers(String email) {
List<String> senders = MainActivity.database.receivedMessagesDao().getSendersForReceiver(email, 0);
List<String> receivers = MainActivity.database.sentMessagesDao().getReceiversForSender(email, 0);
List<String> peers = new ArrayList<>();
peers.addAll(senders);
peers.addAll(receivers);
peers = new ArrayList<>(new HashSet<>(peers));
List<String> sortedPeers = new ArrayList<>();
while (peers.size() > 0) {
LocalDateTime latestMessageTime = null;
String latestPeer = null;
for (String peer : peers) {
List<Integer> receivedMessages = MainActivity.database.receivedMessagesDao().getMessagesToReceiverFromSender(email, peer, 0);
List<Integer> sentMessages = MainActivity.database.sentMessagesDao().getMessagesBySenderToReceiver(email, peer, 0);
LocalDateTime lastMessageTime;
if (sentMessages.size() == 0) {
lastMessageTime = LocalDateTime.parse(MainActivity.database.receivedMessagesDao().getTime(receivedMessages.get(receivedMessages.size() - 1)));
} else if (receivedMessages.size() == 0) {
lastMessageTime = LocalDateTime.parse(MainActivity.database.sentMessagesDao().getTime(sentMessages.get(sentMessages.size() - 1)));
} else {
LocalDateTime lastReceivedMessageTime = LocalDateTime.parse(MainActivity.database.receivedMessagesDao().getTime(receivedMessages.get(receivedMessages.size() - 1)));
LocalDateTime lastSentMessageTime = LocalDateTime.parse(MainActivity.database.sentMessagesDao().getTime(sentMessages.get(sentMessages.size() - 1)));
if (lastReceivedMessageTime.isAfter(lastSentMessageTime)) {
lastMessageTime = lastReceivedMessageTime;
} else {
lastMessageTime = lastSentMessageTime;
}
}
if (latestMessageTime != null) {
if (lastMessageTime.isAfter(latestMessageTime)) {
latestMessageTime = lastMessageTime;
latestPeer = peer;
}
} else {
latestMessageTime = lastMessageTime;
latestPeer = peer;
}
}
sortedPeers.add(latestPeer);
peers.remove(latestPeer);
}
return sortedPeers;
}
public void deleteMessages(final int position) {
recentlyDeletedPeer = sortedPeers.get(position);
recentlyDeletedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages Deleted", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View view) {
sortedPeers.add(recentlyDeletedPeerPosition, recentlyDeletedPeer);
notifyItemInserted(recentlyDeletedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().deleteAllReceivedMessagesFromSender(email, recentlyDeletedPeer);
MainActivity.database.sentMessagesDao().deleteAllSentMessagesFromReceiver(email, recentlyDeletedPeer);
}
}
});
snackbar.show();
}
public void archiveMessages(final int position) {
recentlyArchivedPeer = sortedPeers.get(position);
recentlyArchivedPeerPosition = position;
sortedPeers.remove(position);
notifyItemRemoved(position);
Snackbar snackbar = Snackbar.make(activity.findViewById(R.id.constraint_layout),
"Messages archived", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View view) {
sortedPeers.add(recentlyArchivedPeerPosition, recentlyArchivedPeer);
notifyItemInserted(recentlyArchivedPeerPosition);
}
});
snackbar.setActionTextColor(0xff0099ff);
snackbar.addCallback(new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT || event == Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE) {
MainActivity.database.receivedMessagesDao().archive(email, recentlyArchivedPeer);
MainActivity.database.sentMessagesDao().archive(email, recentlyArchivedPeer);
reload();
}
}
});
snackbar.show();
}
}
And here is the error I get (my classes marked with **):
2020-09-12 19:24:11.547 689-689/com.example.treeapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.treeapp, PID: 689
java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1
at java.util.ArrayList.get(ArrayList.java:439)
**at com.example.treeapp.DonorMessagesAdapter.onBindViewHolder(DonorMessagesAdapter.java:74)
at com.example.treeapp.DonorMessagesAdapter.onBindViewHolder(DonorMessagesAdapter.java:24)**
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1762)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:955)
at android.view.View.layout(View.java:20967)
at android.view.ViewGroup.layout(ViewGroup.java:6440)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3092)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2779)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1863)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8072)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:658)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
To solve your problem no.1 you could try to only check if the dismiss was caused by a click e.g.:
snackbar.addCallback(new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {
MainActivity.database.receivedMessagesDao().deleteAllReceivedMessagesFromSender(email, recentlyDeletedPeer);
MainActivity.database.sentMessagesDao().deleteAllSentMessagesFromReceiver(email, recentlyDeletedPeer);
}
}
});
As for your second problem, the crash happens because you only check if the sentMessages list is empty
if (sentMessages.size() == 0) {
lastMessage = MainActivity.database.receivedMessagesDao().getMessage(receivedMessages.get(receivedMessages.size() - 1)); // line 74 in error message
lastMessageType = "received";
if (MainActivity.database.receivedMessagesDao().getImage(receivedMessages.get(receivedMessages.size() - 1)) == 1) {
lastMessageType = "receivedImage";
}
}
What you forget to do here is to check whether reveivedMessages is also empty.
For example:
if (sentMessages.size() == 0) {
if(receivedMessages.size() != 0) {
lastMessage = MainActivity.database.receivedMessagesDao().getMessage(receivedMessages.get(receivedMessages.size() - 1)); // line 74 in error message
lastMessageType = "received";
if (MainActivity.database.receivedMessagesDao().getImage(receivedMessages.get(receivedMessages.size() - 1)) == 1) {
lastMessageType = "receivedImage";
}
}
}
What happens is your receivedMessages list is also empty which means you try to get the item at position -1, which obviously wont work.
Even if these tips solve your problem you should seriously think about how you access your data in the database, because you seem to execute all of the calls on the UI Thread, which is not advisable since it will block the UI Thread.

Exoplayer2 in recyclerview video pause on notifyitemchange is called

I have the task to make video play in recyclerview use exoplayer
and use this reference :
I am follow tutorial by Droid wafe :
and code works very well in my application. but. there is a slight problem. the problem is when I call the notifyItemChange (position) function to update an item on videoViewHolder. the video paused, but the audio still runs in the background.
I think the problem is when notifyItemChange is called, video view holder in re-create and make the video pause (default) but audio keep running cause not calling setplaywhenready(false)
import android.content.Context;
import android.graphics.Point;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.RequestManager;
import com.google.android.exoplayer2.*;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.*;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
import es.hyrax.zonapets.R;
import es.hyrax.zonapets.data.network.model.post.Post;
import es.hyrax.zonapets.ui.user_private.home.dashboard_my_zona.viewholder_myzona.ViewHolderImages;
import es.hyrax.zonapets.ui.user_private.home.dashboard_my_zona.viewholder_myzona.ViewHolderMultipleImages;
import es.hyrax.zonapets.ui.user_private.home.dashboard_my_zona.viewholder_myzona.ViewHolderVideos;
import es.hyrax.zonapets.utils.listener.OnDoubleClickListener;
import java.util.ArrayList;
import java.util.Objects;
public class ExoPlayerRecyclerViewMyZona extends RecyclerView {
private static final String TAG = "ExoPlayerRecyclerView";
private static final String AppName = "ZonaPets";
/**
* PlayerViewHolder UI component
* Watch PlayerViewHolder class
*/
private ImageView mediaCoverImage, volumeControl;
private FrameLayout container_volume;
private ProgressBar progressBar;
private View viewHolderParent;
private AspectRatioFrameLayout mediaContainer;
private PlayerView videoSurfaceView;
private SimpleExoPlayer videoPlayer;
/**
* variable declaration
*/
// Media List
private ArrayList<Post> mediaObjects = new ArrayList<>();
private int videoSurfaceDefaultHeight = 0;
private int screenDefaultHeight = 0;
private Context context;
private int playPosition = -1;
private boolean isVideoViewAdded;
private RequestManager requestManager;
// controlling volume state
private VolumeState volumeState;
private int targetPosition;
private OnEventClickVideoViewHolder onEventClick;
public ExoPlayerRecyclerViewMyZona(#NonNull Context context) {
super(context);
init(context);
}
public ExoPlayerRecyclerViewMyZona(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
this.context = context.getApplicationContext();
Display display = ((WindowManager) Objects.requireNonNull(
getContext().getSystemService(Context.WINDOW_SERVICE))).getDefaultDisplay();
Point point = new Point();
display.getSize(point);
videoSurfaceDefaultHeight = point.x;
screenDefaultHeight = point.y;
videoSurfaceView = new PlayerView(this.context);
videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
//Create the player using ExoPlayerFactory
videoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
// Disable Player Control
videoSurfaceView.setUseController(false);
// Bind the player to the containerView.
videoSurfaceView.setPlayer(videoPlayer);
// Turn on Volume
setVolumeControl(VolumeState.ON);
addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mediaCoverImage != null) {
// show the old thumbnail
mediaCoverImage.setVisibility(VISIBLE);
}
// There's a special case when the end of the list has been reached.
// Need to handle that with this bit of logic
if (!recyclerView.canScrollVertically(1)) {
playVideo(true);
} else {
playVideo(false);
}
}
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(#NonNull View view) {
}
#Override
public void onChildViewDetachedFromWindow(#NonNull View view) {
if (viewHolderParent != null && viewHolderParent.equals(view)) {
resetVideoView();
}
}
});
videoPlayer.addListener(new Player.EventListener() {
#Override
public void onTimelineChanged(Timeline timeline, #Nullable Object manifest, int reason) {
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups,
TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
switch (playbackState) {
case Player.STATE_BUFFERING:
Log.e(TAG, "onPlayerStateChanged: Buffering video.");
if (progressBar != null) {
progressBar.setVisibility(VISIBLE);
}
if (volumeControl != null){
volumeControl.setAlpha(1f);
}
break;
case Player.STATE_ENDED:
Log.d(TAG, "onPlayerStateChanged: Video ended.");
videoPlayer.seekTo(0);
break;
case Player.STATE_IDLE:
break;
case Player.STATE_READY:
Log.e(TAG, "onPlayerStateChanged: Ready to play.");
if (progressBar != null) {
progressBar.setVisibility(GONE);
}
if (volumeControl != null){
volumeControl.setAlpha(1f);
}
if (!isVideoViewAdded) {
addVideoView();
}
break;
default:
break;
}
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
});
}
public void playVideo(boolean isEndOfList) {
int targetPosition;
if (!isEndOfList) {
int startPosition = ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
// if there is more than 2 list-items on the screen, set the difference to be 1
if (endPosition - startPosition > 1) {
endPosition = startPosition + 1;
}
// something is wrong. return.
if (startPosition < 0 || endPosition < 0) {
return;
}
// if there is more than 1 list-item on the screen
if (startPosition != endPosition) {
int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
targetPosition =
startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
} else {
targetPosition = startPosition;
}
} else {
targetPosition = mediaObjects.size() - 1;
}
Log.d(TAG, "playVideo: target position: " + targetPosition);
// video is already playing so return
if (targetPosition == playPosition) {
return;
}
// set the position of the list-item that is to be played
playPosition = targetPosition;
if (videoSurfaceView == null) {
return;
}
// remove any old surface views from previously playing videos
videoSurfaceView.setVisibility(INVISIBLE);
removeVideoView(videoSurfaceView);
//set get target position to this target position
this.targetPosition = targetPosition;
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return;
}
if (child.getTag() instanceof ViewHolderVideos) {
ViewHolderVideos holder = (ViewHolderVideos) child.getTag();
if (holder == null) {
playPosition = -1;
return;
}
mediaCoverImage = holder.img_post;
progressBar = holder.progressBar;
volumeControl = holder.volumeControl;
viewHolderParent = holder.itemView;
container_volume = holder.container_volume;
requestManager = holder.requestManager;
mediaContainer = holder.mediaContainer;
videoSurfaceView.setPlayer(videoPlayer);
mediaContainer.setOnClickListener(new OnDoubleClickListener() {
#Override
public void onDoubleClick(View v) {
onEventClick.onDoubleClickListener(targetPosition);
}
#Override
public void onSingleClick(View v) {
onEventClick.onSingleClickListener(targetPosition);
}
});
container_volume.setOnClickListener(new OnDoubleClickListener() {
#Override
public void onDoubleClick(View v) {
}
#Override
public void onSingleClick(View v) {
toggleVolume();
}
});
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
context, Util.getUserAgent(context, AppName));
String mediaUrl = mediaObjects.get(targetPosition).getImagepost().get(0).getVideo();/*"https://androidwave.com/media/androidwave-video-1.mp4";*/
if (mediaUrl != null) {
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(mediaUrl));
videoPlayer.prepare(videoSource);
videoPlayer.setPlayWhenReady(true);
}
}else {
videoPlayer.setPlayWhenReady(false);
}
}
/**
* Returns the visible region of the video surface on the screen.
* if some is cut off, it will return less than the #videoSurfaceDefaultHeight
*/
private int getVisibleVideoSurfaceHeight(int playPosition) {
int at = playPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
Log.d(TAG, "getVisibleVideoSurfaceHeight: at: " + at);
View child = getChildAt(at);
if (child == null) {
return 0;
}
int[] location = new int[2];
child.getLocationInWindow(location);
if (location[1] < 0) {
return location[1] + videoSurfaceDefaultHeight;
} else {
return screenDefaultHeight - location[1];
}
}
// Remove the old player
private void removeVideoView(PlayerView videoView) {
ViewGroup parent = (ViewGroup) videoView.getParent();
if (parent == null) {
return;
}
int index = parent.indexOfChild(videoView);
if (index >= 0) {
parent.removeViewAt(index);
isVideoViewAdded = false;
//viewHolderParent.setOnClickListener(null);
}
}
private void addVideoView() {
mediaContainer.addView(videoSurfaceView);
isVideoViewAdded = true;
videoSurfaceView.requestFocus();
videoSurfaceView.setVisibility(VISIBLE);
videoSurfaceView.setAlpha(1);
mediaCoverImage.setVisibility(GONE);
volumeControl.setVisibility(VISIBLE);
}
private void resetVideoView() {
if (isVideoViewAdded) {
removeVideoView(videoSurfaceView);
playPosition = -1;
videoSurfaceView.setVisibility(INVISIBLE);
mediaCoverImage.setVisibility(VISIBLE);
volumeControl.setVisibility(VISIBLE);
}
}
private Boolean currentPositionIsVideoType(){
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return false;
}
if (child.getTag() instanceof ViewHolderVideos) {
return true;
}else {
return false;
}
}
public void releasePlayer() {
if (currentPositionIsVideoType()) {
if (videoPlayer != null) {
videoPlayer.release();
videoPlayer = null;
}
viewHolderParent = null;
}
}
public void onPausePlayer() {
if (currentPositionIsVideoType()){
if (videoPlayer != null) {
//videoPlayer.stop(true);
videoPlayer.setPlayWhenReady(false);
videoPlayer.getPlaybackState();
}
}
}
public void resumePlayer() {
if (currentPositionIsVideoType()){
if (videoPlayer != null) {
videoPlayer.setPlayWhenReady(true);
videoPlayer.getPlaybackState();
}
}
}
private void toggleVolume() {
if (videoPlayer != null) {
if (volumeState == VolumeState.OFF) {
Log.d(TAG, "togglePlaybackState: enabling volume.");
setVolumeControl(VolumeState.ON);
} else if (volumeState == VolumeState.ON) {
Log.d(TAG, "togglePlaybackState: disabling volume.");
setVolumeControl(VolumeState.OFF);
}
}
}
//public void onRestartPlayer() {
// if (videoPlayer != null) {
// playVideo(true);
// }
//}
private void setVolumeControl(VolumeState state) {
volumeState = state;
if (state == VolumeState.OFF) {
videoPlayer.setVolume(0f);
animateVolumeControl();
} else if (state == VolumeState.ON) {
videoPlayer.setVolume(1f);
animateVolumeControl();
}
}
private void animateVolumeControl() {
if (volumeControl != null) {
volumeControl.bringToFront();
if (volumeState == VolumeState.OFF) {
requestManager.load(R.drawable.ic_volume_off)
.into(volumeControl);
} else if (volumeState == VolumeState.ON) {
requestManager.load(R.drawable.ic_volume_on)
.into(volumeControl);
}
//volumeControl.animate().cancel();
volumeControl.setAlpha(1f);
volumeControl.animate()
.alpha(1f)
.setDuration(600).setStartDelay(1000);
}
}
public void setMediaObjects(ArrayList<Post> mediaObjects) {
this.mediaObjects = mediaObjects;
}
public void setVideoViewHolderClickListener(OnEventClickVideoViewHolder onEventClick){
this.onEventClick = onEventClick;
}
/**
* Volume ENUM
*/
private enum VolumeState {
ON, OFF
}
public interface OnEventClickVideoViewHolder {
void onSingleClickListener(int position);
void onDoubleClickListener(int position);
}
}
and log cat not show any error!!
please take a look my comment in issue report for help understand

Mopub: integration with the Millennial Media native ads adapter

Does anyone know how to integrate the Millennial Media native ads adapter?
I read this page: https://support.millennialmedia.com/hc/en-us/articles/205081010-MoPub-Integrations
Downloaded the android adapters and then replace the APID with my APID, but don't know what the DCN is and I was unable to find it on their site.
I think I'm getting the error below because I have not inputted the DCN:
03-24 22:07:12.832 29491-29491/? D/MoPub: Attempting to invoke custom
event: com.mopub.nativeads.MillennialNative 03-24 22:07:12.850
29491-29491/? V/MoPub: Native Ad failed to load with error:
CustomEventNative was configured incorrectly.. 03-24 22:07:13.328
29491-29491/? D/MoPub: Attempting to invoke custom event:
com.mopub.nativeads.MoPubCustomEventNative
This is their adapter I'm using the Millennial Media:
package com.mopub.nativeads;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import com.millennialmedia.AppInfo;
import com.millennialmedia.MMException;
import com.millennialmedia.MMSDK;
import com.millennialmedia.NativeAd;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static com.mopub.nativeads.NativeImageHelper.preCacheImages;
public class MillennialNative extends CustomEventNative {
public static final String DCN_KEY = "dcn";
public static final String APID_KEY = "XXX";
private final static String LOGCAT_TAG = "MoPub->MM-Native";
private static final Handler UI_THREAD_HANDLER = new Handler(Looper.getMainLooper());
public String siteId;
#Override
protected void loadNativeAd(final Activity activity,
final CustomEventNativeListener listener,
final Map<String, Object> localExtras,
final Map<String, String> serverExtras) {
String placementId;
if ( !MMSDK.isInitialized() ) {
try {
MMSDK.initialize(activity);
} catch ( Exception e ) {
Log.e(LOGCAT_TAG, "Unable to initialize the Millennial SDK-- " + e.getMessage());
e.printStackTrace();
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
listener.onNativeAdFailed(NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
return;
}
}
if ( extrasAreValid( serverExtras )) {
placementId = serverExtras.get(APID_KEY);
siteId = serverExtras.get(DCN_KEY);
} else {
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
listener.onNativeAdFailed(NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
return;
}
try {
AppInfo ai = new AppInfo().setMediator("mopubsdk");
if ( siteId != null && siteId.length() > 0 ) {
ai = ai.setSiteId(siteId);
} else {
ai = ai.setSiteId(null);
}
MMSDK.setAppInfo(ai);
} catch ( IllegalStateException e ) {
Log.w(LOGCAT_TAG, "Caught exception: " + e.getMessage());
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
listener.onNativeAdFailed(NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
return;
}
try {
NativeAd nativeAd = NativeAd.createInstance(placementId, NativeAd.NATIVE_TYPE_INLINE);
final MillennialStaticNativeAd millennialStaticNativeAd =
new MillennialStaticNativeAd(activity,
nativeAd,
new ImpressionTracker(activity),
new NativeClickHandler(activity),
listener);
millennialStaticNativeAd.loadAd();
} catch ( MMException e ) {
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
listener.onNativeAdFailed(NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
}
}
private boolean extrasAreValid(final Map<String, String> serverExtras) {
String placementId = serverExtras.get(APID_KEY);
return (serverExtras.containsKey(APID_KEY) &&
placementId != null && placementId.length() > 0 );
}
static class MillennialStaticNativeAd extends StaticNativeAd implements NativeAd.NativeListener {
private final Context mContext;
private NativeAd mNativeAd;
private final ImpressionTracker mImpressionTracker;
private final NativeClickHandler mNativeClickHandler;
private final CustomEventNativeListener mListener;
private final MillennialStaticNativeAd mMillennialStaticNativeAd;
public MillennialStaticNativeAd(final Context context,
final NativeAd nativeAd,
final ImpressionTracker impressionTracker,
final NativeClickHandler nativeClickHandler,
final CustomEventNativeListener customEventNativeListener) {
mContext = context.getApplicationContext();
mNativeAd = nativeAd;
mImpressionTracker = impressionTracker;
mNativeClickHandler = nativeClickHandler;
mListener = customEventNativeListener;
mMillennialStaticNativeAd = this;
nativeAd.setListener(this);
}
void loadAd() {
Log.i(LOGCAT_TAG, "Loading native ad...");
try {
mNativeAd.load(mContext, null);
} catch (MMException e) {
Log.w(MillennialNative.LOGCAT_TAG, "Caught configuration error Exception.");
e.printStackTrace();
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
mListener.onNativeAdFailed(NativeErrorCode
.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
}
}
// Lifecycle Handlers
#Override
public void prepare(final View view) {
// Must access these methods directly to get impressions to fire.
mNativeAd.getIconImage();
mNativeAd.getDisclaimer();
mImpressionTracker.addView(view, this);
mNativeClickHandler.setOnClickListener(view, this);
}
#Override
public void clear(final View view) {
mImpressionTracker.removeView(view);
mNativeClickHandler.clearOnClickListener(view);
}
#Override
public void destroy() {
mImpressionTracker.destroy();
mNativeAd.setListener(null);
mNativeAd = null;
}
// Event Handlers
#Override
public void recordImpression(final View view) {
notifyAdImpressed();
try {
mNativeAd.fireImpression();
Log.i(LOGCAT_TAG, "Millennial native impression recorded.");
} catch ( MMException m ) {
Log.e(LOGCAT_TAG, "Millennial native impression NOT tracked: " + m.getMessage() );
}
}
#Override
public void handleClick(final View view) {
notifyAdClicked();
mNativeClickHandler.openClickDestinationUrl(getClickDestinationUrl(), view);
mNativeAd.fireClicked();
Log.i(LOGCAT_TAG, "Millennial native ad clicked!");
}
// MM'S Native mListener
#Override
public void onLoaded(NativeAd nativeAd) {
// Set assets
String iconImageUrl = nativeAd.getImageUrl(NativeAd.ComponentName.ICON_IMAGE, 1);
String mainImageUrl = nativeAd.getImageUrl(NativeAd.ComponentName.MAIN_IMAGE, 1);
setTitle(nativeAd.getTitle().getText().toString());
setText(nativeAd.getBody().getText().toString());
setCallToAction(nativeAd.getCallToActionButton().getText().toString());
final String clickDestinationUrl = nativeAd.getCallToActionUrl();
if (clickDestinationUrl == null) {
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
Log.d(LOGCAT_TAG,
"Millennial native encountered null destination url. Failing over.");
mListener.onNativeAdFailed(
NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
return;
}
setClickDestinationUrl(clickDestinationUrl);
setIconImageUrl(iconImageUrl);
setMainImageUrl(mainImageUrl);
final List<String> urls = new ArrayList<String>();
if ( iconImageUrl != null ) { urls.add(iconImageUrl); }
if ( mainImageUrl != null ) { urls.add(mainImageUrl); }
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
// This has to be run on the main thread:
preCacheImages(mContext, urls, new NativeImageHelper.ImageListener() {
#Override
public void onImagesCached() {
mListener.onNativeAdLoaded(mMillennialStaticNativeAd);
Log.i(LOGCAT_TAG, "Millennial native ad loaded");
}
#Override
public void onImagesFailedToCache(NativeErrorCode errorCode) {
mListener.onNativeAdFailed(errorCode);
}
});
}
});
}
#Override
public void onLoadFailed(NativeAd nativeAd, NativeAd.NativeErrorStatus nativeErrorStatus) {
final NativeErrorCode error;
switch ( nativeErrorStatus.getErrorCode() ) {
case NativeAd.NativeErrorStatus.LOAD_TIMED_OUT:
error = NativeErrorCode.NETWORK_TIMEOUT;
break;
case NativeAd.NativeErrorStatus.NO_NETWORK:
error = NativeErrorCode.CONNECTION_ERROR;
break;
case NativeAd.NativeErrorStatus.UNKNOWN:
error = NativeErrorCode.UNSPECIFIED;
break;
case NativeAd.NativeErrorStatus.LOAD_FAILED:
case NativeAd.NativeErrorStatus.INIT_FAILED:
error = NativeErrorCode.UNEXPECTED_RESPONSE_CODE;
break;
case NativeAd.NativeErrorStatus.ADAPTER_NOT_FOUND:
error = NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR;
break;
case NativeAd.NativeErrorStatus.DISPLAY_FAILED:
case NativeAd.NativeErrorStatus.EXPIRED:
error = NativeErrorCode.UNSPECIFIED;
break;
default:
error = NativeErrorCode.NETWORK_NO_FILL;
}
UI_THREAD_HANDLER.post(new Runnable() {
#Override
public void run() {
mListener.onNativeAdFailed(error);
}
});
Log.i(LOGCAT_TAG, "Millennial native ad failed: " + nativeErrorStatus.getDescription() );
}
#Override
public void onClicked(NativeAd nativeAd, NativeAd.ComponentName componentName, int i) {
Log.i(LOGCAT_TAG, "Millennial native SDK's click tracker fired.");
}
#Override
public void onAdLeftApplication(NativeAd nativeAd) {
Log.i(LOGCAT_TAG, "Millennial native SDK has left the application.");
}
#Override
public void onExpired(NativeAd nativeAd) {
Log.i(LOGCAT_TAG, "Millennial native ad has expired!");
}
}
}

Calling functions such as startActivity() inside of an enum

I wanted to centralise the creation of DialogFragments used to report errors to the user in order to just have a class to which I pass the error code and have the dialog spawned automagically.
In order to handle multiple errors I am using an enum in which I define the error propreties.
public enum DialogError {
TTS_NOT_INSTALLED {
#Override
public int getTitleResource() {
return R.string.error_tts_not_installed_title;
}
#Override
public int getMessageResource() {
return R.string.error_tts_not_installed_message;
}
#Override
public int getPositiveButtonResource() {
return R.string.error_tts_not_installed_button_positive;
}
#Override
public void onPositiveButtonClick() {
// TODO
}
#Override
public int getNegativeButtonResource() {
return R.string.error_tts_not_installed_button_negative;
}
#Override
public void onNegativeButtonClick() {
// TODO
}
};
public abstract int getTitleResource();
public abstract int getMessageResource();
public abstract int getPositiveButtonResource();
public abstract void onPositiveButtonClick();
public abstract int getNegativeButtonResource();
public abstract void onNegativeButtonClick();
}
Then I have my FragmentDialogError class that I call to create a new Dialog.
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
public class FragmentDialogError
extends DialogFragment {
Context context;
DialogError error;
public FragmentDialogError(Context context, DialogError error) {
this.context = context;
this.error = error;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder
.setTitle(error.getTitleResource())
.setMessage(error.getMessageResource())
.setPositiveButton(error.getPositiveButtonResource(),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
error.onPositiveButtonClick();
}
})
.setNegativeButton(error.getNegativeButtonResource(),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
error.onNegativeButtonClick();
}
});
return builder.create();
}
}
My problem now is that I can't obviously call functions such as startActivity inside of my enum's onPositiveButtonClick() or onNegativeButtonClick().
One soluction would be using a switch() in FragmentDialogError but this way I would split the code between the enum and the class. Another one would be to define in some way the actions that a button press could trigger and let handle them to another class, but I'm looking for a clean and elegant soluction.
How can I implement this in Java keeping the code tidy?
Why not just add a Context to the onClick handlers that you can use to do startActivity?
Also, instead of overriding all the methods in your enum why not use members and a constructor?
public enum DialogError {
TTS_NOT_INSTALLED(
R.string.error_tts_not_installed_title,
R.string.error_tts_not_installed_message,
R.string.error_tts_not_installed_button_positive,
R.string.error_tts_not_installed_button_negative) {
public void onPositiveButtonClick(Context context) {
context.startActivity...
}
#Override
public void onNegativeButtonClick(Context context) {
// TODO
}
};
private final int mTitle;
private final int mMessage;
private final int mPositive;
private final int mNegative;
private DialogError(int title, int message, int positive, int negative) {
mTitle = title;
mMessage = message;
mPositive = positive;
mNegative = negative;
}
public final int getTitleResource() {
return mTitle;
}
public final int getMessageResource() {
return mMessage;
}
public final int getPositiveButtonResource() {
return mPositive;
}
public final int getNegativeButtonResource() {
return mNegative;
}
public abstract void onPositiveButtonClick();
public abstract void onNegativeButtonClick();
}

How to custom set a user list to be displayed in FriendPickerFragment

I'm working on an application where I use the Facebook SDK. I've integrated Facebook SDK in my app. I'm trying to display Facebook friends for the logged in user. I've would not like to show to the user all his Facebook friend, but only those who meet a certain criteria. I've managed to choose these Facebook friends, but not able to display them.
Is there a way to custom display the Facebook friends ?
In my code, in order to fetch all Facebook users I user picker://friend uri. I've also tried to pass certain user IDs to that Uri, but with no success. Is there a was to pass Facebook Ids to picker://friend so I can display certain friends, or this direction is wrong?
Thanks in advance for you help.
Alex
This is an old question, but I couldn't find anyone else who had tried to do this. I was able to subclass FriendPickerFragment to specify the user IDs, by overriding the graph request and its handler. It's a bit hacky but it seems to work.
// Has to be in this package so we can access package-private members on the superclass.
package com.facebook.widget;
import android.os.Bundle;
import android.text.TextUtils;
import com.facebook.Request;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphUser;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Created by Derek on 1/11/2015.
*/
public class MyFriendPickerFragment extends FriendPickerFragment {
private List<String> userIds;
public void setUserIds(List<String> userIds) {
this.userIds = userIds;
}
#Override
Request getRequestForLoadData(Session session) {
if (userIds == null) {
return super.getRequestForLoadData(session);
} else {
return createRequest(extraFields, session);
}
}
private Request createRequest(Set<String> extraFields, Session session) {
Request request = Request.newGraphPathRequest(session, "", null);
Set<String> fields = new HashSet<String>(extraFields);
String[] requiredFields = new String[]{
"id",
"name"
};
fields.addAll(Arrays.asList(requiredFields));
String pictureField = adapter.getPictureFieldSpecifier();
if (pictureField != null) {
fields.add(pictureField);
}
Bundle parameters = request.getParameters();
parameters.putString("fields", TextUtils.join(",", fields));
parameters.putString("ids", TextUtils.join(",", userIds));
request.setParameters(parameters);
return request;
}
#Override
public void loadData(boolean forceReload) {
if(userIds == null) {
super.loadData(forceReload);
} else {
Request request = getRequestForLoadData(getSession());
request.setCallback(
new Request.Callback() {
#Override
public void onCompleted(Response response) {
if (response.getError() == null) {
Map<String, Object> map = response.getGraphObject().asMap();
final List<GraphUser> users = new ArrayList<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
JSONObject jsonObject = (JSONObject)entry.getValue();
GraphUser user = GraphObject.Factory.create(jsonObject, GraphUser.class);
users.add(user);
}
adapter.changeCursor(new GraphObjectCursor<GraphUser>() {
private int position = 0;
#Override
public boolean isFromCache() {
return false;
}
#Override
public boolean areMoreObjectsAvailable() {
return position < getCount() - 1;
}
#Override
public int getCount() {
return users.size();
}
#Override
public int getPosition() {
return position;
}
#Override
public boolean move(int offset) {
return moveToPosition(position + offset);
}
#Override
public boolean moveToPosition(int newPosition) {
if (newPosition >= 0 && newPosition < getCount()) {
position = newPosition;
return true;
}
return false;
}
#Override
public boolean moveToFirst() {
position = 0;
return true;
}
#Override
public boolean moveToLast() {
position = getCount() - 1;
return true;
}
#Override
public boolean moveToNext() {
return move(1);
}
#Override
public boolean moveToPrevious() {
return move(-1);
}
#Override
public boolean isFirst() {
return position == 0;
}
#Override
public boolean isLast() {
return position == getCount() - 1;
}
#Override
public boolean isBeforeFirst() {
return position < 0;
}
#Override
public boolean isAfterLast() {
return position >= getCount();
}
#Override
public GraphUser getGraphObject() {
return users.get(position);
}
#Override
public void close() {
}
#Override
public boolean isClosed() {
return false;
}
});
}
}
}
);
request.executeAsync();
}
}
}

Categories

Resources