I have a Fragment with RecyclerView, which holds items with ImageView (basically like gallery app). Images are displayed using async task to do the work in separate thread. Images are displayed from base64 encoded string. Images are also cached with lrucache.
Problem
Everything works fine until i rotate the device 3rd or 4th time. The device crashes with out of memory error in onSaveInstanceState method.
Question
Any ideas how to prevent the OutOfMemory error? Thanks in advance
Code
Activity
public class TabsActivity extends BaseActivity implements ViewPager.OnPageChangeListener,
ActivityActions, TabLayout.OnTabSelectedListener {
private static final String TAG = TabsActivity.class.getSimpleName();
private static final String STATE_TAB_LAYOUT = "STATE_TAB_LAYOUT";
private static final String STATE_TOOLBAR = "STATE_TOOLBAR";
public static final String ACTION_TASKS = "ACTION_TASKS";
public static final String ACTION_MESSAGES = "ACTION_MESSAGES";
public static final int REQUEST_TASK_UPDATE = 1;
public static final int REQUEST_MESSAGE_UPDATE = 2;
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager viewPager;
private FloatingActionButton createButton;
private PrefsManager prefsManager;
private ViewPagerAdapter adapter;
private LocalBroadcastManager broadcastManager;
private NotificationReceiver notificationReceiver;
private FragmentManager.OnBackStackChangedListener backStackChangedListener;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tabs);
prefsManager = PrefsManager.getInstance(this);
notificationReceiver = new NotificationReceiver();
backStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
onSettingsFragmentStateChanged(false);
}
}
};
tabLayout = (TabLayout) findViewById(R.id.tab_layout);
toolbar = (Toolbar) findViewById(R.id.toolbar);
viewPager = (ViewPager) findViewById(R.id.view_pager);
createButton = (FloatingActionButton) findViewById(R.id.floating_action_button);
createButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
KeyboardUtils.hideSoftwareInput(TabsActivity.this);
if (adapter.getCurrentTab(viewPager.getCurrentItem()) == Tab.TASKS) {
createNewTask();
} else {
createNewConversation();
}
}
});
setSupportActionBar(toolbar);
toolbar.setNavigationIcon(R.drawable.ic_action_back);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
final String action = getIntent().getAction();
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.setAdapterListener(new ExtendedPagerAdapter.AdapterListener() {
#Override
public void onAdapterInstantiated() {
if (savedInstanceState == null && action != null) {
if (action.equals(ACTION_TASKS)) {
onPageSelected(viewPager.getCurrentItem());
} else {
viewPager.setCurrentItem(1);
}
}
}
});
viewPager.addOnPageChangeListener(this);
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.setOnTabSelectedListener(this);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_TASK_UPDATE) {
if (resultCode == Activity.RESULT_OK) {
((TasksFragment) adapter.getFragment(Tab.TASKS)).onTasksUpdated();
}
} else if (requestCode == REQUEST_MESSAGE_UPDATE) {
TabFragment tabFragment = adapter.getFragment(Tab.MESSAGES);
if (resultCode == Activity.RESULT_OK) {
if (tabFragment != null) {
((MessagesFragment) tabFragment).onNewMessagesReceived();
}
} else {
((MessagesFragment) tabFragment).initData();
}
}
super.onActivityResult(requestCode, resultCode, data);
}
#Override
protected void onResume() {
broadcastManager = LocalBroadcastManager.getInstance(this);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BroadcastConfig.ACTION_NEW_MESSAGE);
intentFilter.addAction(BroadcastConfig.ACTION_USER_STATUS);
broadcastManager.registerReceiver(notificationReceiver, intentFilter);
getSupportFragmentManager().addOnBackStackChangedListener(backStackChangedListener);
super.onResume();
}
#Override
protected void onPause() {
broadcastManager.unregisterReceiver(notificationReceiver);
getSupportFragmentManager().removeOnBackStackChangedListener(backStackChangedListener);
super.onPause();
}
#Override
protected void onRestoreInstanceState(Bundle inState) {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(inState.getString(STATE_TOOLBAR));
}
if (!inState.getBoolean(STATE_TAB_LAYOUT)) {
onSettingsFragmentStateChanged(true);
}
super.onRestoreInstanceState(inState);
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(STATE_TAB_LAYOUT, tabLayout.getVisibility() == View.VISIBLE);
outState.putString(STATE_TOOLBAR, toolbar.getTitle().toString());
super.onSaveInstanceState(outState);
}
#Override
public void onBackPressed() {
MenuItem menuItem = toolbar.getMenu().findItem(R.id.action_search);
if (menuItem != null && !((SearchView) menuItem.getActionView()).isIconified()) {
((SearchView) menuItem.getActionView()).onActionViewCollapsed();
return;
}
if (popSupportBackStack(SettingsFragment.class.getSimpleName())) {
return;
}
setResult(RESULT_OK);
finish();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_contacts, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
adapter.getFragment(viewPager.getCurrentItem()).onSearchPhraseChanged(newText);
return true;
}
});
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
onSettingsClick();
break;
case R.id.action_refresh:
onRefreshClick();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onPageSelected(int position) {
if (adapter.isInstantiated()) {
onToolbarTitleChanged(adapter.getPageTitle(position).toString());
onToolbarSubtitleChanged(UserStatus.NONE);
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
#Override
public void onTabSelected(TabLayout.Tab tab) {
if (adapter.getCurrentTab(tab.getPosition()).getFragmentTitle()
== Tab.ATTACHMENT_HISTORY.getFragmentTitle()) {
createButton.hide();
}
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(final TabLayout.Tab tab) {
createButton.hide(new FloatingActionButton.OnVisibilityChangedListener() {
#Override
public void onHidden(FloatingActionButton fab) {
fab.show();
}
});
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
#Override
public void requestDisplayDetails(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode);
}
#Override
public void refreshTasks() {
adapter.getFragment(Tab.TASKS).onRefresh();
}
#Override
public void refreshMessages() {
adapter.getFragment(Tab.MESSAGES).onRefresh();
}
#Override
public boolean isNetworkAvailable() {
return checkNetworkAvailability();
}
private void createNewTask() {
startActivityForResult(new Intent(this, CreateTaskActivity.class), REQUEST_TASK_UPDATE);
}
private void createNewConversation() {
MessagesFragment fragment = (MessagesFragment) adapter.getFragment(Tab.MESSAGES);
startActivityForResult(new Intent(TabsActivity.this, MessageDetailsActivity.class)
.setAction(MessageDetailsActivity.ACTION_CREATE_MESSAGE)
.putExtra(MessageDetailsActivity.EXTRA_EXIST_CONV,
fragment.getCreatedConversations()), REQUEST_MESSAGE_UPDATE);
}
private void setTabLayoutVisible(boolean visible) {
int visibility = visible ? View.VISIBLE : View.GONE;
tabLayout.setVisibility(visibility);
}
private void onRefreshClick() {
if (checkNetworkAvailability()) {
adapter.getFragment(viewPager.getCurrentItem()).onRefresh();
}
}
private void onSettingsClick() {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, new SettingsFragment(), SettingsFragment.class.getSimpleName())
.addToBackStack(SettingsFragment.class.getSimpleName())
.commit();
onSettingsFragmentStateChanged(true);
}
private void onSettingsFragmentStateChanged(boolean visibleState) {
if (visibleState) {
isFragmentDialog = true;
onToolbarTitleChanged(getString(R.string.title_settings));
setTabLayoutVisible(false);
createButton.hide();
} else {
onPageSelected(viewPager.getCurrentItem());
setTabLayoutVisible(true);
if (adapter.getCurrentTab(viewPager.getCurrentItem()) != Tab.ATTACHMENT_HISTORY) {
createButton.show();
}
}
}
public void onToolbarTitleChanged(String title) {
toolbar.setTitle(title);
}
public void onToolbarSubtitleChanged(UserStatus userStatus) {
if (userStatus != null) {
toolbar.setSubtitle(userStatus.getText());
toolbar.setSubtitleTextColor(userStatus.getColor());
}
}
public void onLogoutConfirmed() {
HttpRequestManager.logout(prefsManager.getPhpSessId(), App.getPhoneId(this), prefsManager.getUserId(),
new HttpCallback<LogoutResponse>() {
#Override
public void onResponse(LogoutResponse logoutResponse) {
prefsManager.reset();
DBManager.delete(TabsActivity.this, DeleteTask.DeleteType.ALL, new Callback<Boolean>() {
#Override
public void onResponseReceived(Boolean params) {
startActivity(new Intent(TabsActivity.this, LoginActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
finish();
}
});
}
});
}
private class NotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BroadcastConfig.ACTION_NEW_MESSAGE) || action.equals(BroadcastConfig.ACTION_USER_STATUS)) {
TabFragment childFragment = adapter.getFragment(Tab.MESSAGES);
if (childFragment != null) {
((MessagesFragment) childFragment).onNewMessagesReceived();
}
}
}
}
private class ViewPagerAdapter extends ExtendedPagerAdapter {
private final List<Tab> tabs = Tab.getAllTabs();
private final List<TabFragment> fragments = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
fragments.add((TabFragment) super.instantiateItem(container, position));
return fragments.get(fragments.size() - 1);
}
#Override
public Fragment getItem(int position) {
Log.e(TAG, "CreatingFragment: " + tabs.get(position).getFragmentClass().getCanonicalName());
return Fragment.instantiate(TabsActivity.this, tabs.get(position).getFragmentClass().getCanonicalName());
}
#Override
public CharSequence getPageTitle(int position) {
return getString(tabs.get(position).getFragmentTitle());
}
#Override
public int getCount() {
return tabs.size();
}
public Tab getCurrentTab(int position) {
return tabs.get(position);
}
public TabFragment getFragment(int position) {
if (getCount() > position) {
return fragments.get(position);
}
return null;
}
public TabFragment getFragment(Tab tab) {
for (TabFragment tabFragment : fragments) {
if (tab.getFragmentClass() == tabFragment.getClass()) {
return tabFragment;
}
}
return null;
}
}
}
Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
swipeRefresh = (SwipeRefresh) inflater.inflate(R.layout.fragment_recycler_view, container, false);
galleryView = (RecyclerView) swipeRefresh.findViewById(R.id.recycler_view);
return swipeRefresh;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
swipeRefresh.setOnRefreshListener(this);
galleryView.setHasFixedSize(true);
galleryView.setLayoutManager(new GridLayoutManager(getContext(), getSpanCount()));
galleryView.setAdapter(adapter = new Adapter(attachments));
if (savedInstanceState == null) {
onRefresh();
} else {
onRestoreInstanceState(savedInstanceState);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
try {
outState.putString(STATE_ATTACHMENTS, Json.fromObject(attachments)); //Crashes here after 3rd or 4th rotate
} catch (JsonProcessingException e) {
Log.e(TAG, "Error while saving attachments", e);
}
super.onSaveInstanceState(outState);
}
#Override
public void onSearchPhraseChanged(String phrase) {
}
#Override
public void onRefresh() {
swipeRefresh.setRefreshing(true);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
initSampleData();
}
}, 3000);
}
private void onRestoreInstanceState(Bundle savedInstanceState) {
try {
attachments = Json.toCollection(savedInstanceState.getString(STATE_ATTACHMENTS),
ArrayList.class, Attachment.class);
adapter.notifyDataSetChanged();
} catch (IOException e) {
Log.e(TAG, "Error while restoring attachments", e);
}
}
private void initSampleData() {
swipeRefresh.setRefreshing(false);
String image = "";
File path = new File(Environment.getExternalStorageDirectory(), "image.txt");
byte[] bytes = new byte[(int) path.length()];
try {
FileInputStream fileInputStream = new FileInputStream(path);
fileInputStream.read(bytes);
image = new String(bytes);
} catch (FileNotFoundException e) {
Log.e(TAG, "Error while finding file to read from", e);
} catch (IOException e) {
Log.e(TAG, "Error while writing from file to string", e);
}
Attachment attachment = new Attachment(image);
attachments.clear();
for (int i = 0; i < 100; i++) {
attachment.setFileName(String.valueOf(i));
attachments.add(attachment);
}
adapter.notifyDataSetChanged();
}
private int getSpanCount() {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float width = displayMetrics.widthPixels / displayMetrics.density;
float height = displayMetrics.heightPixels / displayMetrics.density;
int size;
if (Math.min(width, height) >= 600) {
size = Math.round(width / THUMBNAIL_SIZE_TABLET);
} else {
size = Math.round(width / THUMBNAIL_SIZE_PHONE);
}
return size < 7 ? size : 6;
}
private class ViewHolder extends BaseHolder<Attachment> implements View.OnClickListener {
private ImageView imageView;
public ViewHolder(ViewGroup viewGroup, int layoutRes) {
super(viewGroup, layoutRes);
imageView = (ImageView) itemView;
}
#Override
public void bind(Attachment attachment) {
itemView.setOnClickListener(this);
if (attachment.getImage() != null && !attachment.getImage().isEmpty()) {
DisplayThumbnailRequest.loadBitmap(attachment, imageView);
}
}
#Override
public void onClick(View v) {
// TODO: 2016-01-15 Open image in fullscreen
}
}
private class Adapter extends RecyclerView.Adapter<ViewHolder> {
private List<Attachment> attachments;
public Adapter(List<Attachment> attachments) {
this.attachments = attachments;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(parent, R.layout.adapter_item_attachment_history);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(attachments.get(position));
}
#Override
public int getItemCount() {
return attachments.size();
}
}
AsyncTask
public class DisplayThumbnailRequest extends AsyncHttpTask<Attachment, Void, Bitmap> {
private static final String TAG = DisplayThumbnailRequest.class.getSimpleName();
private WeakReference<ImageView> imageViewRef;
private Attachment attachment;
public DisplayThumbnailRequest(ImageView imageView) {
this.imageViewRef = new WeakReference<>(imageView);
}
#Override
protected void onPreExecute() {
if (imageViewRef != null) {
ImageView imageView = imageViewRef.get();
if (imageView != null) {
if (imageView.getVisibility() != View.VISIBLE) {
imageView.setVisibility(View.VISIBLE);
}
imageView.setImageResource(R.mipmap.ic_launcher);
}
}
}
#Override
protected Bitmap doInBackground(Attachment... params) {
attachment = params[0];
Bitmap bitmap = BitmapCache.getBitmap(attachment.getFileName());
if (bitmap == null) {
bitmap = BitmapUtils.fromBase64(attachment.getImage());
BitmapCache.addBitmap(attachment.getFileName(), bitmap);
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewRef != null && bitmap != null) {
ImageView imageView = imageViewRef.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
#Override
protected void onResponseReceived() {
}
public Attachment getAttachment() {
return attachment;
}
public static void loadBitmap(Attachment attachment, ImageView imageView) {
if (cancelDownloadRequest(attachment, imageView)) {
DisplayThumbnailRequest request = new DisplayThumbnailRequest(imageView);
AsyncBitmapDrawable drawable = new AsyncBitmapDrawable(App.getRes(), null, request);
imageView.setImageDrawable(drawable);
request.execute(attachment);
}
}
private static boolean cancelDownloadRequest(Attachment attachment, ImageView imageView) {
DisplayThumbnailRequest request = getDownloadTask(imageView);
if (request != null) {
String filePath = request.getAttachment().getFileName();
if (filePath == null || filePath.isEmpty() || !filePath.equals(attachment.getFileName())) {
request.cancel(true);
} else {
return false;
}
}
return true;
}
private static DisplayThumbnailRequest getDownloadTask(ImageView imageView) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncBitmapDrawable) {
return ((AsyncBitmapDrawable) drawable).getDisplayThumbnailRequest();
}
}
return null;
}
}
BitmapCache
public class BitmapCache {
private static final String TAG = BitmapCache.class.getSimpleName();
private static final int MAX_MEMORY = (int)((Runtime.getRuntime().maxMemory() / 1024) / 4);
private static LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(MAX_MEMORY) {
#Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount() / 1024;
}
};
public static void addBitmap(String key, Bitmap bitmap) {
if (getBitmap(key) == null) {
lruCache.put(key, bitmap);
}
}
public static Bitmap getBitmap(String key) {
return lruCache.get(key);
}
}
BitmapUtils.fromBase64
public static Bitmap fromBase64(String string) {
if (string != null && !string.isEmpty()) {
byte[] decodedString = Base64.decode(string, Base64.DEFAULT);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length, options);
options.inSampleSize = getInSampleSize(options, Metrics.dp2px(100), Metrics.dp2px(100));
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length, options);
}
return null;
}
EDIT
I tried reducing arraylist size which is saved in onSavedInstanceState from 100 to 50 and OOM error is not being displayed (despite how many times you rotate the device). Can it be, that saved instance is too long (if it list has 100 items) and it floods the memory?
The problem is located in your
onRefresh() method.
Your should remove all calbacks from handler if activity is beeing stopped.
Try this:
private Handler handler = new Handler();
#Override
public void onRefresh() {
swipeRefresh.setRefreshing(true);
handler.postDelayed(new Runnable() {
#Override
public void run() {
initSampleData();
}
}, 3000);
}
#Override
protected void onStop() {
super.onStop();
handler.removeCallbacksAndMessages(null);
}
Related
I have 2 Activities in my application.
activityMain contain 3 Fragments
The third fragment is a conversations list. This is a recylerView where each Item leads to a specific chat.
activityConversation contains a Chat.
First, i would like to sort the conversations in the the recyclerView in order of "Last actives". The most recent active should be displayed on top of the list, the second last active on second postition etc...
Secondly, each Item of the recyclerView contains a Textview. For each item, I would like to display the last message posted in the related chat in this Texview.
Finally, i would like to display these Item textViews in Bold since the conversation has not been opened until the last chat update.
Has anyone an Idea to help me achieve that?
Here my Chat Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_conversation);
getWindow().setBackgroundDrawableResource(R.drawable._background_black_lchatxxxhdpi) ;
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.open, R.string.close);
mDrawerLayout.addDrawerListener(mToggle);
mToggle.syncState();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
LinearLayout leftNav = (LinearLayout)findViewById(R.id.conv_left_nav);
LinearLayout helperAdmin = (LinearLayout) getLayoutInflater().inflate(R.layout.list_participant_admin, leftNav, false);
leftNav.addView(helperAdmin);
final EditText input_post = (EditText)findViewById(R.id.input_post);
context = this;
input_post.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ImageButton btn_submit = (ImageButton)findViewById(R.id.btn_submit);
btn_submit.setEnabled(!TextUtils.isEmpty(s.toString().trim()));
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
Intent intent = getIntent();
if (intent.hasExtra("conversationId"))
conversationId = intent.getStringExtra("conversationId");
rpcHelper = new RPCHelper(context, this);
String unique_device_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
Log.i("US", unique_device_id);
rpcHelper.loginOrRegister(unique_device_id, new AsyncResponseListener() {
#Override
public void onResponse(JSONArray response) throws JSONException {
refreshConversation();
}
#Override
public void onResponse() {
}
#Override
public void onResponse(Bitmap bm) {
}
#Override
public void onPreExecute() {
}
});
dbHelper = new DataBaseHelper(context, "us", null, Statics.DB_VERSION);
userInConv = dbHelper.dbReader.getUserInConversation(Integer.parseInt(conversationId));
storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
leftRecycler = (RecyclerView) helperAdmin.findViewById(R.id.conv_left_recycler);
//mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
leftLayoutManager = new LinearLayoutManager(this);
leftRecycler.setLayoutManager(leftLayoutManager);
leftAdapter = new ConvLeftAdapter(userInConv, storageDir, Integer.parseInt(conversationId));
leftRecycler.setAdapter(leftAdapter);
helpersImg = new View[3];
helpers = new DataBaseReader.User[3];
photos = dbHelper.dbReader.getPhotosInConversation(Integer.parseInt(conversationId));
photoRecycler = (RecyclerView) findViewById(R.id.photo_recycler);
photoLayoutManager = new GridLayoutManager(this, Math.max(photos.length, 1));
photoRecycler.setLayoutManager(photoLayoutManager);
rightAdapter = new ConvRightAdapter(photos, storageDir, context);
photoRecycler.setAdapter(rightAdapter);
IntentFilter filter = new IntentFilter(Statics.ACTION_NEW_POST);
this.registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context ctx, Intent intent) {
Log.d("new", " message");
refreshConversation();
}
}, filter);
}
#Override
public void onNewIntent(Intent intent){
if (intent.hasExtra("conversationId"))
conversationId = intent.getStringExtra("conversationId");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_conversation, menu);
DataBaseReader.Conversation conversation = dbHelper.dbReader.getConversation(conversationId);
DataBaseReader.User owner = dbHelper.dbReader.getConversationOwner(conversationId);
final ImageView owner_img = (ImageView)findViewById(R.id.img_userprofilpic);
TextView owner_name = (TextView)findViewById(R.id.lbl_username_owner);
TextView owner_city = (TextView)findViewById(R.id.lbl_usercity_owner);
TextView conversation_question = (TextView)findViewById(R.id.question_text);
owner_name.setText(owner.name);
owner_city.setText(owner.city);
conversation_question.setText(conversation.question.text);
conversation_question.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView text = (TextView)findViewById(R.id.question_text);
int maxLines = TextViewCompat.getMaxLines(text);
if (maxLines==2){
text.setMaxLines(Integer.MAX_VALUE);
}
else{
text.setMaxLines(2);
}
}
});
rpcHelper.getPhoto(storageDir + "/", owner.photo, new AsyncResponseListener() {
#Override
public void onResponse(JSONArray response) throws JSONException {
}
#Override
public void onResponse() {
}
#Override
public void onResponse(Bitmap bm) {
owner_img.setImageBitmap(bm);
}
#Override
public void onPreExecute() {
}
});
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(mToggle.onOptionsItemSelected(item)){
return true;
}
return super.onOptionsItemSelected(item);
}
public void refreshConversation(){
dbHelper.dbSyncer.syncPosts(rpcHelper.user_id, new AsyncResponseListener() {
#Override
public void onResponse(JSONArray response) throws JSONException {
DataBaseReader.Post[] posts = dbHelper.dbReader.getPosts(conversationId);
postsRecycler = (RecyclerView) findViewById(R.id.posts_recycler);
postsLayoutManager = new LinearLayoutManager(context);
postsRecycler.setLayoutManager(postsLayoutManager);
postsAdapter = new PostsAdapter(posts, storageDir, rpcHelper);
postsRecycler.setAdapter(postsAdapter);
postsRecycler.scrollToPosition(postsAdapter.getItemCount() - 1);
}
#Override
public void onResponse() {
}
#Override
public void onResponse(Bitmap bm) {
}
#Override
public void onPreExecute() {
}
});
/*
rpcHelper.getPosts(conversationId, new AsyncResponseListener(){
#Override
public void onResponse(JSONArray response) throws JSONException {
LinearLayout posts_root = (LinearLayout) findViewById(R.id.posts_root);
posts_root.removeAllViews();
for (int i = 0; i < response.length(); i++){
Log.d("Conv refresh", response.get(i) + "");
final JSONObject jConversation = (JSONObject) response.get(i);
LinearLayout post;
if (jConversation.getString("userId") == rpcHelper.user_id) {
post = (LinearLayout) getLayoutInflater().inflate(R.layout.item_chatpost_sent, posts_root, false);
}
else{
post = (LinearLayout) getLayoutInflater().inflate(R.layout.item_chatpost_received, posts_root, false);
((TextView)post.findViewById(R.id.post_name_)).setText(jConversation.getString("name"));
}
((TextView)post.findViewById(R.id.lbl_message_chat)).setText(jConversation.getString("text"));
posts_root.addView(post);
}
hideProcessDialog();
}
#Override
public void onResponse() {
}
#Override
public void onResponse(Bitmap bm) {
}
#Override
public void onPreExecute() {
}
});*/
}
public void onSubmit(View v){
final EditText input_post = (EditText)findViewById(R.id.input_post);
String post_text = input_post.getText().toString();
rpcHelper.post(conversationId, post_text, new AsyncResponseListener() {
#Override
public void onResponse(JSONArray response) throws JSONException {
refreshConversation();
input_post.setText("");
}
#Override
public void onResponse() {
}
#Override
public void onResponse(Bitmap bm) {
}
#Override
public void onPreExecute() {
}
});
}
Button no = (Button)alertDialog.findViewById(R.id.btn_cancel);
no.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
helpersImg[0] = null;
helpersImg[1] = null;
helpersImg[2] = null;
helpers[0] = null;
helpers[1] = null;
helpers[2] = null;
alertDialog.dismiss();
}
});
}
public void onSelectUser(View v){
View vi = snapHelper.findSnapView(participantsLayoutManager);
if (helpersImg[0] == vi || helpersImg[1] == vi || helpersImg[2] == vi)
return;
Log.i("get helper Id", ""+ participantsAdapter.selectedUserId);
ImageView photo = (ImageView) vi.findViewById(R.id.img_userprofilpic);
photo.setDrawingCacheEnabled(true);
Bitmap bmap = photo.getDrawingCache();
ImageView helperImage = null;
if (helpersImg[0] == null) {
helperImage = (ImageView) alertDialog.findViewById(R.id.reward_dialog_helper0);
helpersImg[0] = vi;
helperImage.setImageBitmap(bmap);
photo.setColorFilter(Color.rgb(123, 123, 123), android.graphics.PorterDuff.Mode.MULTIPLY);
helpers[0] = userInConv[participantsAdapter.selectedUserId];
}
else if (helpersImg[1] == null){
helperImage = (ImageView) alertDialog.findViewById(R.id.reward_dialog_helper1);
helpersImg[1] = vi;
helperImage.setImageBitmap(bmap);
photo.setColorFilter(Color.rgb(123, 123, 123), android.graphics.PorterDuff.Mode.MULTIPLY);
helpers[1] = userInConv[participantsAdapter.selectedUserId];
}
else if (helpersImg[2] == null){
helperImage = (ImageView) alertDialog.findViewById(R.id.reward_dialog_helper2);
helpersImg[2] = vi;
helperImage.setImageBitmap(bmap);
photo.setColorFilter(Color.rgb(123, 123, 123), android.graphics.PorterDuff.Mode.MULTIPLY);
helpers[1] = userInConv[participantsAdapter.selectedUserId];
}
else{
return;
}
}
/**private void showTipDialog(){
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
LayoutInflater inflater = LayoutInflater.from(context);
final View dialogView = inflater.inflate(R.layout.dialog_add_tip, null);
final EditText value = (EditText) dialogView.findViewById(R.id.tip_value);
final SeekBar sb = (SeekBar) dialogView.findViewById(R.id.seekBar);
sb.setMax(50);
sb.setProgress(5);
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
progress = (Math.round(progress/5 ))*5;
seekBar.setProgress(progress);
value.setText(String.valueOf(progress));
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
value.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) {
// Convert text to integer. Do you already use editText.setInputType(InputType.TYPE_CLASS_NUMBER), don't you?
Integer enteredProgress = Integer.valueOf(s.toString());
sb.setProgress(enteredProgress);
}
#Override
public void afterTextChanged(Editable s) {}});
dialogBuilder.setView(dialogView);
alertDialog = dialogBuilder.create();
alertDialog.show();
Button ok = (Button)alertDialog.findViewById(R.id.btn_ok);
ok.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
alertDialog.dismiss();
}
});
Button no = (Button)alertDialog.findViewById(R.id.btn_no);
no.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
alertDialog.dismiss();
}
});
}*/
public void removeHelper(View v){
int index = 0;
if (v == alertDialog.findViewById(R.id.reward_dialog_helper0)){
index = 0;
}
else if (v == alertDialog.findViewById(R.id.reward_dialog_helper1)){
index = 1;
}
else if (v == alertDialog.findViewById(R.id.reward_dialog_helper2)){
index = 2;
}
if (helpersImg[index] == null){
return;
}
ImageView photo = (ImageView) helpersImg[index].findViewById(R.id.img_userprofilpic);
photo.setDrawingCacheEnabled(true);
photo.clearColorFilter();
helpersImg[index] = null;
helpers[index] = null;
ImageView imv = (ImageView)v;
imv.setImageResource(R.drawable.stroke_rounded_corners_white);
}
private void showProcessDialog(){
pd = new ProgressDialog(this);
pd.setTitle("Processing");
pd.setMessage("Please wait...");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
}
private void hideProcessDialog(){
pd.hide();
}
#Override
public void onInternetConnectionLost() {
}
#Override
public void onInternetConnectionFound() {
}
public void onTakePicture(View v){
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, Statics.REQUEST_IMAGE_CAPTURE);
}
}
public void onTakePictureFromGallery(View v){
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
"Select Picture"), Statics.REQUEST_PROFILE_IMAGE_GALLERY);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Bitmap imageBitmap;
if ((requestCode == Statics.REQUEST_IMAGE_CAPTURE || requestCode == Statics.REQUEST_IMAGE_CAPTURE_0
|| requestCode == Statics.REQUEST_IMAGE_CAPTURE_1 || requestCode == Statics.REQUEST_IMAGE_CAPTURE_2) && resultCode == RESULT_OK && data != null) {
Bundle extras = data.getExtras();
if (extras == null){
return;
}
imageBitmap = (Bitmap) extras.get("data");
addPhoto(imageBitmap);
}
else if (requestCode == Statics.REQUEST_PROFILE_IMAGE_GALLERY && resultCode == RESULT_OK){
try {
imageBitmap = getBitmapFromUri(data.getData());
addPhoto(imageBitmap);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void addPhoto(Bitmap image) {
DataBaseReader.Conversation c = dbHelper.dbReader.getConversation(conversationId);
String encodedImage = encodeBitmap(image);
rpcHelper.addPhotosToQuestion("" + c.question.id, encodedImage, null, null, new AsyncResponseListener() {
#Override
public void onResponse(JSONArray response) throws JSONException {
dbHelper.dbSyncer.sync(rpcHelper.user_id, new AsyncResponseListener() {
#Override
public void onResponse(JSONArray response) throws JSONException {
photos = dbHelper.dbReader.getPhotosInConversation(Integer.parseInt(conversationId));
photoRecycler = (RecyclerView) findViewById(R.id.photo_recycler);
photoLayoutManager = new GridLayoutManager(context, Math.max(photos.length, 1));
photoRecycler.setLayoutManager(photoLayoutManager);
rightAdapter = new ConvRightAdapter(photos, storageDir, context);
photoRecycler.setAdapter(rightAdapter);
}
#Override
public void onResponse() {
photos = dbHelper.dbReader.getPhotosInConversation(Integer.parseInt(conversationId));
photoRecycler = (RecyclerView) findViewById(R.id.photo_recycler);
photoLayoutManager = new GridLayoutManager(context, Math.max(photos.length, 1));
photoRecycler.setLayoutManager(photoLayoutManager);
rightAdapter = new ConvRightAdapter(photos, storageDir, context);
photoRecycler.setAdapter(rightAdapter);
}
#Override
public void onResponse(Bitmap bm) {
}
#Override
public void onPreExecute() {
}
});
}
#Override
public void onResponse() {
}
#Override
public void onResponse(Bitmap bm) {
}
#Override
public void onPreExecute() {
}
});
}
private String encodeBitmap(Bitmap bitmap){
try{
bitmap = Bitmap.createScaledBitmap(bitmap, Statics.BITMAP_WIDTH, Statics.BITMAP_HEIGHT, true);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
final byte[] imageInByte = stream.toByteArray();
return Base64.encodeToString(imageInByte, Base64.DEFAULT);
}
catch(Exception e){
return "";
}
}
private Bitmap getBitmapFromUri(Uri uri) throws IOException {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
return image;
}
}
This is my Fragment with conversations List:
public class ConversationFragment extends Fragment {
private View v;
private OnFragmentInteractionListener mListener;
public ConversationFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #return A new instance of fragment ConversationFragment.
*/
// TODO: Rename and change types and number of parameters
public static ConversationFragment newInstance() {
ConversationFragment fragment = new ConversationFragment();
Bundle args = new Bundle();
//args.putString(ARG_PARAM1, param1);
//args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (v == null) {
v = inflater.inflate(R.layout.fragment_conversation, container, false);
}
final SwipeRefreshLayout swipeRefresh = (SwipeRefreshLayout)v.findViewById(R.id.swiperefreshconv);
swipeRefresh.post(new Runnable() {
#Override
public void run() {
swipeRefresh.setRefreshing(true);
}
});
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
mListener.syncDb();
}
});
return v;
}
#Override
public void onStart(){
super.onStart();
mListener.refreshConversations();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
}
This is my Conversation Adapter:
public class ConversationsAdapter extends RecyclerView.Adapter {
private final File mStorageDir;
private final RPCHelper mRPCHelper;
private DataBaseReader.Conversation[] mDataset;
Context context;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public View mView;
public ViewHolder(View v) {
super(v);
mView = v;
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public ConversationsAdapter(DataBaseReader.Conversation[] myDataset, File storageDir) {
mDataset = myDataset;
mStorageDir = storageDir;
mRPCHelper = new RPCHelper();
}
// Create new views (invoked by the layout manager)
#Override
public ConversationsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_conversations, parent, false);
ViewHolder vh = new ViewHolder(v);
context = parent.getContext();
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
Log.d("recy", "bind called");
TextView username = (TextView)holder.mView.findViewById(R.id.lbl_username);
final TextView message = (TextView)holder.mView.findViewById(R.id.question_text);
TextView date_and_time = (TextView)holder.mView.findViewById(R.id.lbl_date_and_time);
ImageView status_pending = (ImageView)holder.mView.findViewById(R.id.lbl_status_conversation_pending);
ImageView status_in = (ImageView)holder.mView.findViewById(R.id.lbl_status_conversation_in);
TextView keyword0 = (TextView)holder.mView.findViewById(R.id.post_keywords0);
TextView keyword1 = (TextView)holder.mView.findViewById(R.id.post_keywords1);
TextView keyword2 = (TextView)holder.mView.findViewById(R.id.post_keywords2);
ImageView userprofilpic = (ImageView)holder.mView.findViewById(R.id.img_userprofilpic);
LinearLayout answer_info = (LinearLayout) holder.mView.findViewById(R.id.answer_info);
Button delete_coversation = (Button) holder.mView.findViewById(R.id.btn_confirm_delete);
userprofilpic.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(context, UserProfileActivity.class);
i.putExtra("userId", mDataset[position].question.userId);
context.startActivity(i);
}
});
username.setText(mDataset[position].question.userName);
message.setText(mDataset[position].question.text);
keyword0.setText(mDataset[position].question.keywords[0]);
keyword1.setText(mDataset[position].question.keywords[1]);
keyword2.setText(mDataset[position].question.keywords[2]);
addImgToView(mDataset[position].question.photo, userprofilpic);
if (Integer.parseInt(mDataset[position].confirmed) == 1) {
status_pending.setEnabled(false);
status_pending.setVisibility(View.GONE);
status_in.setVisibility(View.VISIBLE);
answer_info.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
message.setTypeface(Typeface.DEFAULT);
message.onSaveInstanceState();
int convId = mDataset[position].id;
Intent i = new Intent(context, ConversationActivity.class);
i.putExtra("conversationId", "" + convId);
context.startActivity(i);
}
});
}
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
private void addImgToView(final String uri, final ImageView v){
mRPCHelper.getPhoto(mStorageDir + "/", uri, new AsyncResponseListener() {
#Override
public void onResponse(JSONArray response) throws JSONException {
}
#Override
public void onResponse() {
}
#Override
public void onResponse(Bitmap bm) {
v.setImageBitmap(bm);
}
#Override
public void onPreExecute() {
}
});
}
}
Thank you in advance for your time.
maintain a flag in data level to know is it read or unread,based on that you can apply the styles.
There is two way to do this.
First you can ask to backend to write server side to query to handle last activity bases of timestamp .In this case you have to send your particular timestamp to server when you open particular conversation .
Other you can make local database ex-(sql database) and handle it in your own code by updating query when you reading or undreading conversation.
I have a button in every cell of a RecyclerView that launches a download network call. The cell displays differently according to whether it's downloading, downloaded or finished.
my simplified code :
#Override public void onBindViewHolder(final CatalogViewHolder holder, int position) {
final DownloadStatusCallback statusCallback = new DownloadStatusCallback() {
#Override public void started() {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
holder.itemView.setBackground(//color1
}
});
}
#Override public void finished() {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
holder.itemView.setBackground(//color 2
}
});
}
#Override public void error(Exception e) {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
holder.itemView.setBackground(//color 3
}
});
}
};
holder.button1.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
assyncCall(statusCallback);
}
});
}
The first time i clic on a cell, everything works fine. If I clic on the download button of another cell, both of them will update.
I understand that's due to recyclerview recycling cells, but I can't figure out how to do better.
Thanks !
my full adapter :
public class CatalogRecyclerAdapter extends RecyclerView.Adapter<CatalogViewHolder> {
public static final String TAG = "CatalogRecyclerAdapter";
private final LayoutInflater inflater;
private final DownloadCenter downloadCenter;
private final ListInterface.FlowController flowController;
private final ResourcesStringRepository resourcesStringRepository;
private final ImageManagerFactory imageManagerFactory;
private final Handler mainThreadHandler;
public CatalogRecyclerAdapter(LayoutInflater inflater, ListInterface.FlowController flowController,
DownloadCenter downloadCenter, ResourcesStringRepository resourcesStringRepository,
ImageManagerFactory imageManagerFactory, Handler mainThreadHandler) {
this.inflater = inflater;
this.flowController = flowController;
this.downloadCenter = downloadCenter;
this.resourcesStringRepository = resourcesStringRepository;
this.imageManagerFactory = imageManagerFactory;
this.mainThreadHandler = mainThreadHandler;
}
private static final int TITLE = 0;
private static final int USER = 2;
private static final int PROGRAM = 3;
private static final int COURSE = 4;
private static final int GROUP = 5;
private static final int MEDIA = 6;
private static final int ERROR = 7;
private static final int DEMO = 8;
//The list of all elements
private List<FilterableUser> users = new ArrayList<>();
private List<CatalogProgram> programs = new ArrayList<>();
private List<CatalogProgram> demos = new ArrayList<>();
private List<CatalogCourse> courses = new ArrayList<>();
private List<FilterableGroup> groups = new ArrayList<>();
private List<CatalogMedia> medias = new ArrayList<>();
//The list that will be displayed after filtering and research.
List<Object> displayedList = new ArrayList<>();
static final String TITLES[] = new String[10];
static {
Context ctx = M360Application.getContext();
TITLES[USER] = ctx.getString(R.string.users);
TITLES[PROGRAM] = ctx.getString(R.string.programs);
TITLES[COURSE] = ctx.getString(R.string.courses);
TITLES[GROUP] = ctx.getString(R.string.groups);
TITLES[MEDIA] = ctx.getString(R.string.documents);
TITLES[DEMO] = ctx.getString(R.string.programs_demo);
}
private String searchString;
#Override public int getItemViewType(int position) {
if (displayedList.get(position) instanceof String) {
return TITLE;
} else if (displayedList.get(position) instanceof FilterableUser) {
return USER;
} else if (displayedList.get(position) instanceof CatalogProgramDemo) {
return DEMO;
} else if (displayedList.get(position) instanceof CatalogProgram) {
return PROGRAM;
} else if (displayedList.get(position) instanceof CatalogCourse) {
return COURSE;
} else if (displayedList.get(position) instanceof FilterableGroup) {
return GROUP;
} else if (displayedList.get(position) instanceof CatalogMedia) {
return MEDIA;
} else if (displayedList.get(position) instanceof CatalogError) {
return ERROR;
} else {
throw new ClassCastException(
"this adapter's displayedList is corrupted" + displayedList.get(position).toString());
}
}
public void setData(List<Filterable> data, String searchedString) {
searchString = searchedString;
setData(data);
}
private void setData(List<Filterable> data) {
LogDev.i(TAG, "setting data size: " + data.size());
groups.clear();
users.clear();
programs.clear();
demos.clear();
courses.clear();
medias.clear();
for (Filterable element : data) {
if (element instanceof CatalogCourse) {
courses.add((CatalogCourse) element);
} else if (element instanceof FilterableUser) {
users.add((FilterableUser) element);
} else if (element instanceof CatalogProgramDemo) {
demos.add((CatalogProgramDemo) element);
} else if (element instanceof CatalogProgram) {
programs.add((CatalogProgram) element);
} else if (element instanceof FilterableGroup) {
groups.add((FilterableGroup) element);
} else if (element instanceof CatalogMedia) {
medias.add((CatalogMedia) element);
}
}
constructDataSet();
}
private void constructDataSet() {
displayedList.clear();
if (!demos.isEmpty()) {
displayedList.add(TITLES[DEMO]);
displayedList.addAll(demos);
}
if (!programs.isEmpty()) {
displayedList.add(TITLES[PROGRAM]);
displayedList.addAll(programs);
}
if (!courses.isEmpty()) {
displayedList.add(TITLES[COURSE]);
displayedList.addAll(courses);
}
if (!users.isEmpty()) {
displayedList.add(TITLES[USER]);
displayedList.addAll(users);
}
if (!groups.isEmpty()) {
displayedList.add(TITLES[GROUP]);
displayedList.addAll(groups);
}
if (!medias.isEmpty()) {
displayedList.add(TITLES[MEDIA]);
displayedList.addAll(medias);
}
if (displayedList.isEmpty()) {
displayedList.add(new CatalogError());
}
LogDev.w(TAG, "displayedList.size() : " + displayedList.size());
notifyDataSetChanged();
}
#Override public CatalogViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TITLE:
return new TitleViewHolder(inflater.inflate(R.layout.item_list_title_catalog, parent, false));
case USER:
return new UserViewHolder(inflater.inflate(R.layout.widget_user_small, parent, false));
case PROGRAM:
case DEMO:
return new ProgramViewHolder(inflater.inflate(R.layout.widget_program_small, parent, false));
case COURSE:
return new CourseViewHolder(inflater.inflate(R.layout.widget_course_small, parent, false));
case GROUP:
return new GroupViewHolder(inflater.inflate(R.layout.widget_group_small, parent, false));
case MEDIA:
return new MediaViewHolder(inflater.inflate(R.layout.widget_media_small, parent, false));
case ERROR:
return new CatalogErrorViewHolder(inflater.inflate(R.layout.widget_noresult_small, parent, false));
default:
LogDev.e(TAG, "view type not supported");
return null;
}
}
#Override public void onBindViewHolder(CatalogViewHolder holder, int position) {
Object displayedObject = displayedList.get(position);
//holder.bind(displayedObject, errorDisplayInterface);
if (holder instanceof TitleViewHolder && displayedObject instanceof String) {
((TitleViewHolder) holder).tv.setText((String) displayedObject);
} else if (holder instanceof ProgramViewHolder && displayedObject instanceof CatalogProgram) {
bindProgramViewHolder((ProgramViewHolder) holder, (CatalogProgram) displayedObject);
} else if (holder instanceof CourseViewHolder && displayedObject instanceof CatalogCourse) {
bindCourseViewHolder((CourseViewHolder) holder, (CatalogCourse) displayedObject);
} else if (holder instanceof GroupViewHolder && displayedObject instanceof FilterableGroup) {
bindGroupViewHolder((GroupViewHolder) holder, (FilterableGroup) displayedObject);
} else if (holder instanceof UserViewHolder && displayedObject instanceof FilterableUser) {
bindUserViewHolder((UserViewHolder) holder, (FilterableUser) displayedObject);
} else if (holder instanceof MediaViewHolder && displayedObject instanceof CatalogMedia) {
bindMediaViewHolder((MediaViewHolder) holder, (CatalogMedia) displayedObject);
} else if (holder instanceof CatalogErrorViewHolder) {
//No binding with any data
} else {
throw new ClassCastException(displayedObject.toString());
}
//Highlight
if (searchString != null && !searchString.isEmpty())
{
TextViewHighlighter.highlight(holder, searchString);
}
}
private void bindCourseViewHolder(final CourseViewHolder courseViewHolder, final CatalogCourse course) {
courseViewHolder.name_textView.setText(course.name);
courseViewHolder.viewNb_textView.setText(course.views != null ? course.views.toString() : "0");
if (course.elementCount == null) {
courseViewHolder.counterLinear.setVisibility(View.GONE);
} else {
courseViewHolder.counterLinear.setVisibility(View.VISIBLE);
courseViewHolder.questionNb_textView.setText(
course.elementCount.questions != null ? course.elementCount.questions.toString() : "0");
courseViewHolder.mediaNb_textView.setText(
course.elementCount.medias != null ? course.elementCount.medias.toString() : "0");
courseViewHolder.sheetNb_textView.setText(
course.elementCount.sheets != null ? course.elementCount.sheets.toString() : "0");
}
imageManagerFactory.course(course.id).thumbnail(courseViewHolder.pic_imageView);
//new CourseImageManager(course.id).load(courseViewHolder.pic_imageView);
View.OnClickListener clickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToCourse(course.id);
}
};
courseViewHolder.container.setOnClickListener(clickListener);
if (course.canBeOffline) {
courseViewHolder.downloadBlock.setVisibility(View.VISIBLE);
DownloadState state = downloadCenter.getCourseStatus(course.id);
LogDev.i(TAG, "can be offline " + state.name());
if (state == DownloadState.DOWNLOADING) {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADING);
}
if (state == DownloadState.TO_DOWNLOAD) {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADABLE);
}
if (state == DownloadState.DOWNLOADED || state == DownloadState.DOWNLOADED_WITH_SHARED_MODE) {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADED);
} else {
DownloadStatusCallback statusCallback = new DownloadStatusCallback() {
#Override public void started() {
LogDev.i(TAG, "started");
mainThreadHandler.post(new Runnable() {
#Override public void run() {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADING);
}
});
}
#Override public void finished() {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADED);
}
});
}
#Override public void error(Exception e) {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
updateDownloadBlock(courseViewHolder, DownloadableStatus.ERROR);
}
});
}
};
downloadCenter.subscribe(course.id, statusCallback);
courseViewHolder.downloadBlock.setOnClickListener(new View.OnClickListener()
{
#Override public void onClick(View v) {
new Thread() {
#Override public void run() {
super.run();
try {
downloadCenter.downloadCourse(course.id, null);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
});
}
} else {
LogDev.i(TAG, "can't be offline");
courseViewHolder.downloadBlock.setVisibility(View.INVISIBLE);
}
}
private void updateDownloadBlock(CourseViewHolder courseViewHolder, DownloadableStatus status) {
if (status == null) return;
courseViewHolder.downloadBlock.setVisibility(
status.equals(DownloadableStatus.NOT_DOWNLOADABLE) ? View.GONE : View.VISIBLE);
courseViewHolder.downloadImage.setVisibility(
status.equals(DownloadableStatus.DOWNLOADABLE) ? View.VISIBLE : View.GONE);
courseViewHolder.downloadProgress.setVisibility(
status.equals(DownloadableStatus.DOWNLOADING) ? View.VISIBLE : View.GONE);
courseViewHolder.downloadedImage.setVisibility(
status.equals(DownloadableStatus.DOWNLOADED) ? View.VISIBLE : View.GONE);
courseViewHolder.downloadErrImage.setVisibility(
status.equals(DownloadableStatus.ERROR) ? View.VISIBLE : View.GONE);
}
private enum DownloadableStatus {
NOT_DOWNLOADABLE, DOWNLOADABLE, DOWNLOADING, DOWNLOADED, ERROR
}
private void bindProgramViewHolder(ProgramViewHolder programViewHolder, final CatalogProgram program) {
imageManagerFactory.program(program.id).thumbnail(programViewHolder.pic_imageView);
//new ProgramImageManager(program.id).load(programViewHolder.pic_imageView);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToProgram(program.id);
}
};
programViewHolder.container.setOnClickListener(onClickListener);
programViewHolder.pic_imageView.setOnClickListener(onClickListener);
programViewHolder.title_textView.setText(program.name);
programViewHolder.viewCount_textView.setText(program.views != null ? program.views.toString() : "0");
}
private void bindUserViewHolder(UserViewHolder userViewHolder, final FilterableUser user) {
userViewHolder.name_textView.setText(user.name);
userViewHolder.job_textView.setText(user.description);
imageManagerFactory.user(user.id).thumbnail(userViewHolder.pic_imageView);
//new UserImageManager(user.id).loadProfilePic(userViewHolder.pic_imageView, NetworkUtils.isNetworkAvailable(),
// true);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToUser(user.id);
}
};
userViewHolder.pic_imageView.setOnClickListener(onClickListener);
userViewHolder.container.setOnClickListener(onClickListener);
}
private void bindMediaViewHolder(MediaViewHolder mediaViewHolder, final CatalogMedia media) {
imageManagerFactory.media(media.id, media.type, media.extention).symbolOnThumbnail(mediaViewHolder.complex);
//new MediaImageManager(media).load(mediaViewHolder.pic_imageView, NetworkUtils.isNetworkAvailable(), false);
mediaViewHolder.title_textView.setText(media.title);
mediaViewHolder.authorName_textView.setText(media.authorName);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(final View view) {
flowController.routeToDocument(media.id);
}
};
mediaViewHolder.complex.setOnClickListener(onClickListener);
mediaViewHolder.container.setOnClickListener(onClickListener);
}
private void bindGroupViewHolder(GroupViewHolder groupViewHolder, final FilterableGroup group) {
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToGrouop(group.id);
}
};
groupViewHolder.pic_imageView.setOnClickListener(onClickListener);
groupViewHolder.container.setOnClickListener(onClickListener);
groupViewHolder.name_textView.setText(group.name);
String str = resourcesStringRepository.getQuantityString(R.plurals.catalog_group_stat_program,
group.nbProgramsRunning, group.nbProgramsRunning);
str += " - " + resourcesStringRepository.getQuantityString(R.plurals.catalog_group_stat_user, group.nbUser,
group.nbUser);
groupViewHolder.stats_textView.setText(str);
imageManagerFactory.group(group.id).thumbnail(groupViewHolder.pic_imageView);
//new GroupImageManager(group.id).load(groupViewHolder.pic_imageView, NetworkUtils.isNetworkAvailable(), true);
}
#Override public int getItemCount() {
return displayedList.size();
}
}
It is recycling the views.So while clicking the button you have to store its position and change views accordingly.
Maintain a position storing variable globally like this
private int itemClicked=-1;
While clicking the view store the position into itemclicked
holder.button1.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
itemclicked=position;
assyncCall(statusCallback);
}
});
Then while updating views check if the position is same like this
if(position==itemclicked){
//show download for clicked view
}else{
//show download stopped for other views
}
Solution
As Surender and Trickcy Solution suggested, I updated the presented data and then tell the adapter to update the cell accordingly :
DownloadStatusCallback statusCallback = new DownloadStatusCallback() {
#Override public void started() {
LogDev.i(TAG, "started");
course.downloadState = DownloadState.DOWNLOADING;
final int position = courseViewHolder.getAdapterPosition();
mainThreadHandler.post(new Runnable() {
#Override public void run() {
notifyItemChanged(position);
}
});
}
#Override public void finished() {
course.downloadState = DownloadState.DOWNLOADED;
final int position = courseViewHolder.getAdapterPosition();
mainThreadHandler.post(new Runnable() {
#Override public void run() {
notifyItemChanged(position);
}
});
}
#Override public void error(Exception e) {
course.downloadState = DownloadState.ERROR_WHILE_DOWNLOADING;
final int position = courseViewHolder.getAdapterPosition();
mainThreadHandler.post(new Runnable() {
#Override public void run() {
notifyItemChanged(position);
}
});
}
};
I'm new in this android world. I'm currently working on android project i.e., music player but i got stopped at one point since my recycler view doesn't display anything which is supposed to be a list of songs.
I even checked my logcat but cannot figured out whether the data is binding or not.Any kind of help will be grateful.
SongListAdapter.java
public class SongListAdapter extends RecyclerView.Adapter<SongListAdapter.MyViewHolder> {
ArrayList<SongDetailsJDO> mSongDetailsJDOs;
LayoutInflater mLayoutInflater;
Context mContext;
private static final String TAG = "SongListAdapter";
private boolean mIsSongPlaying = false;
private String mCurrentSongId = "-1";
public SongListAdapter(Context context, ArrayList<SongDetailsJDO> pSongDetailsJDOs) {
mContext = context;
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSongDetailsJDOs = pSongDetailsJDOs;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View lView = mLayoutInflater.inflate(R.layout.recycler_view_item, parent, false);
return new MyViewHolder(lView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Uri lUri = null;
if (mSongDetailsJDOs.get(position).getAlbumId() != null && !mSongDetailsJDOs.get(position).getAlbumId().equals("")) {
lUri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), Long.parseLong(mSongDetailsJDOs.get(position).getAlbumId()));
Picasso.with(mContext).load(lUri).resize(100, 100).placeholder(R.drawable.placeholder).into(holder.albumImageIV);
} else
holder.albumImageIV.setImageResource(R.drawable.placeholder);
String lTrackName = mSongDetailsJDOs.get(position).getTitle();
if (lTrackName != null)
holder.trackNameTV.setText(lTrackName.trim());
else
holder.trackNameTV.setText("<Unknown>");
String lAlbumName = mSongDetailsJDOs.get(position).getAlbumName();
if (lAlbumName != null)
holder.albumAndArtistDetailsTV.setText(lAlbumName.trim());
else
holder.albumAndArtistDetailsTV.setText("<Unknown>");
if (mSongDetailsJDOs.get(position).getFavouriteStatus() == 1)
holder.favouriteIV.setImageResource(R.drawable.fav);
else
holder.favouriteIV.setImageResource(R.drawable.fav_u);
// TODO: #holder.animationDrawable use it change Visibility and start (Animation)
if (mIsSongPlaying && mSongDetailsJDOs.get(position).getSongId().equals(mCurrentSongId)) {
holder.eqIv.setVisibility(View.VISIBLE);
holder.animationDrawable = (AnimationDrawable) holder.eqIv.getBackground();
holder.animationDrawable.start();
} else {
holder.eqIv.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
if (mSongDetailsJDOs != null)
return mSongDetailsJDOs.size();
else
return 0;
}
/**
* Called when data is being updated in DB
*/
public void favChanged(int pPosition, int pFavStatus) {
mSongDetailsJDOs.get(pPosition).setFavouriteStatus(pFavStatus);
notifyItemChanged(pPosition);
}
/**
* View Holder class for Rec view
*/
class MyViewHolder extends RecyclerView.ViewHolder {
ImageView albumImageIV;
TextView trackNameTV;
TextView albumAndArtistDetailsTV;
ImageView favouriteIV;
ImageView eqIv;
AnimationDrawable animationDrawable;
MyViewHolder(View itemView) {
super(itemView);
albumImageIV = (ImageView) itemView.findViewById(R.id.album_artwork_iv);
trackNameTV = (TextView) itemView.findViewById(R.id.title_name_tv);
albumAndArtistDetailsTV = (TextView) itemView.findViewById(R.id.artist_author_name_tv);
favouriteIV = (ImageView) itemView.findViewById(R.id.fav_iv);
eqIv = (ImageView) itemView.findViewById(R.id.eq_iv);
}
}
/**
* Swap the data with the new JDO list
*
* #param pSongDetailsJDOs
*/
public void swapData(ArrayList<SongDetailsJDO> pSongDetailsJDOs) {
mSongDetailsJDOs = pSongDetailsJDOs;
notifyDataSetChanged();
}
/**
* Returns the list of currently loaded JDO's
* #return
*/
public List<SongDetailsJDO> getData() {
return mSongDetailsJDOs;
}
/**
* Gets the #{#link SongDetailsJDO} object at the specified position
* #param pPosition
* #return the {#link SongDetailsJDO} object
*/
public SongDetailsJDO getItemAtPosition(int pPosition) {
return mSongDetailsJDOs.get(pPosition);
}
/**
* Update Song Play status
* #param pStatus the status weather is playing or not
* #param lSongId the song id the playing song
*/
public void updateSongPlayStatus(boolean pStatus, String lSongId) {
mIsSongPlaying = pStatus;
mCurrentSongId = lSongId;
notifyDataSetChanged();
}
}
SongListActivity.java
public class SongsListActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>, SharedPreferences.OnSharedPreferenceChangeListener {
private RecyclerView mRecyclerView;
private SongListAdapter mAdapter;
private ArrayList<SongDetailsJDO> mSongDetailsJDOs;
private TextView mNoSongTV;
private static final int LOADER_ID = 101;
private int REQUEST_CODE = 102;
private static final String TAG = "SongsListActivity";
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mPrefEditor;
private SharedPreferences.OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener;
private FirebaseAnalytics mFirebaseAnalytics;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.rec_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(SongsListActivity.this));
mNoSongTV = (TextView) findViewById(R.id.no_song_tv);
mSongDetailsJDOs = new ArrayList<>();
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
mPrefEditor = mSharedPreferences.edit();
mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
FirebaseApp.initializeApp(this);
mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
loadData();
}
private void loadData() {
FirebaseCrash.report(new Exception("OMG An Exception"));
boolean lIsAppLoadingFirstTime = mSharedPreferences.getBoolean(getString(R.string.is_app_loading_first_time), true);
if (lIsAppLoadingFirstTime) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0);
} else {
mPrefEditor.putBoolean(getString(R.string.is_app_loading_first_time), false);
mPrefEditor.apply();
new LoadDataToDbBackground().execute();
// TODO: Create Loader here
}
} else {
loadDataToRecyclerView();
if (mSharedPreferences.getBoolean(getString(R.string.is_song_playing), false)) {
// TODO: Create Loader here
SongDetailsJDO lJDO = getSongJDO(mSharedPreferences.getString(getString(R.string.song_id), ""));
startActivityForResult(new Intent(SongsListActivity.this, PlayerActivity.class)
.putExtra(getString(R.string.song_jdo), lJDO), REQUEST_CODE);
}
}
}
private class LoadDataToDbBackground extends AsyncTask<Void, Integer, Void> {
ProgressDialog mProgressDialog;
#Override
protected void onPreExecute() {
mProgressDialog = new ProgressDialog(SongsListActivity.this);
mProgressDialog.setMessage("Please Wait");
mProgressDialog.setTitle("Loading");
mProgressDialog.show();
super.onPreExecute();
}
#Override
protected void onPostExecute(Void aVoid) {
mProgressDialog.dismiss();
super.onPostExecute(aVoid);
}
#Override
protected Void doInBackground(Void... params) {
CommonHelper lHelper = new CommonHelper();
lHelper.loadSongToDB(SongsListActivity.this);
runOnUiThread(new Runnable() {
#Override
public void run() {
loadDataToRecyclerView();
}
});
return null;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == 0) {
boolean lGranted = true;
for (int lResult : grantResults) {
if (lResult == PackageManager.PERMISSION_DENIED)
lGranted = false;
}
if (lGranted)
loadData();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
if (data != null && data.getExtras() != null && resultCode == PlayerActivity.RESULT_CODE) {
//if data changed reload the recyclerView
if (data.getBooleanExtra(getString(R.string.is_data_changed), false)) {
mSongDetailsJDOs = new SongDetailTable(this).getAllSongs();
mAdapter.swapData(mSongDetailsJDOs);
}
}
}
// updateCurrentSongIndication();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater lMenuInflater = getMenuInflater();
lMenuInflater.inflate(R.menu.menu_song_list, menu);
SearchManager lSearchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
SearchView lSearchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
lSearchView.setSearchableInfo(lSearchManager.getSearchableInfo(getComponentName()));
lSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
filterRecView(newText);
return true;
}
});
return true;
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.TITLE + " ASC");
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
ArrayList<SongDetailsJDO> lSongDetailsNew = new ArrayList<>();
if (data.moveToFirst()) {
do {
lSongDetailsNew.add(new SongDetailsJDO(data.getString(data.getColumnIndex(MediaStore.Audio.Media.TITLE)),
data.getString(data.getColumnIndex(MediaStore.Audio.Media.ALBUM)),
data.getString(data.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)),
data.getString(data.getColumnIndex(MediaStore.Audio.Media._ID)),
data.getInt(data.getColumnIndex(MediaStore.Audio.Media.DURATION)), 0));
} while (data.moveToNext());
}
compareDataAndMakeChangesToDB(lSongDetailsNew);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.d(TAG, "onWindowFocusChanged: ");
updateCurrentSongIndication();
}
private void updateCurrentSongIndication() {
if (mSharedPreferences.getBoolean(getString(R.string.is_song_playing), false)) {
mAdapter.updateSongPlayStatus(true, mSharedPreferences.getString(getString(R.string.song_id), ""));
mRecyclerView.smoothScrollToPosition(getPositionOfSongId(mSharedPreferences.getString(getString(R.string.song_id), "")));
} else {
if(mAdapter!=null)
mAdapter.updateSongPlayStatus(false, "-1");
}
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
updateCurrentSongIndication();
}
private void compareDataAndMakeChangesToDB(ArrayList<SongDetailsJDO> pSongDetailsNew) {
Log.d(TAG, "compareDataAndMakeChangesToDB: Called ============");
ArrayList<String> lSongIdsToBeDeleted = new ArrayList<>();
for (SongDetailsJDO lSongDetailsJDO : mSongDetailsJDOs) {
lSongIdsToBeDeleted.add(lSongDetailsJDO.getSongId());
}
ArrayList<SongDetailsJDO> lNewSongsToBeAdded = new ArrayList<>();
for (SongDetailsJDO lSongDetailsJDO : pSongDetailsNew) {
if (lSongIdsToBeDeleted.contains(lSongDetailsJDO.getSongId())) {
lSongIdsToBeDeleted.remove(lSongDetailsJDO.getSongId());
} else
lNewSongsToBeAdded.add(lSongDetailsJDO);
}
if (lSongIdsToBeDeleted.size() > 0 || lNewSongsToBeAdded.size() > 0) {
SongDetailTable lSongDetailTable = new SongDetailTable(this);
lSongDetailTable.removeSongsForIds(lSongIdsToBeDeleted);
lSongDetailTable.insertSongs(lNewSongsToBeAdded);
loadDataToRecyclerView();
//
// SongPlayerService lSongPlayerService = SongPlayerService.getRunningInstance();
// if (lSongPlayerService != null)
// lSongPlayerService.dataChanged();
}
}
public void onFavClick(View pView) {
//Firebase Logging
Bundle lBundle = new Bundle();
lBundle.putString(FirebaseAnalytics.Param.ITEM_CATEGORY,"Favourite Clicked");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.VIEW_ITEM,lBundle);
int lPosition = mRecyclerView.getChildLayoutPosition((View) pView.getParent());
SongDetailsJDO lSongDetailsJDO = mAdapter.getItemAtPosition(lPosition);
String lSongId = lSongDetailsJDO.getSongId();
SongDetailTable lSongDetailTable = new SongDetailTable(this);
int lNewFavStatus = lSongDetailsJDO.getFavouriteStatus() == 0 ? 1 : 0;
lSongDetailTable.setFavouriteStatus(lSongId, lNewFavStatus);
mAdapter.favChanged(lPosition, lNewFavStatus);
SongPlayerService mSongPlayerService = SongPlayerService.getRunningInstance();
if (mSongPlayerService != null)
mSongPlayerService.favChanged(lPosition, lNewFavStatus);
}
public void onRowClick(View pView) {
int lPosition = mRecyclerView.getChildLayoutPosition(pView);
SongDetailsJDO lJDO = mAdapter.getItemAtPosition(lPosition);
startActivityForResult(new Intent(SongsListActivity.this, PlayerActivity.class)
.putExtra(getString(R.string.song_jdo), lJDO), REQUEST_CODE);
overridePendingTransition(R.anim.from_right, R.anim.scale_down);
}
private SongDetailsJDO getSongJDO(String pSongId) {
SongDetailsJDO lJDO = null;
for (SongDetailsJDO lSongDetailsJDO : mSongDetailsJDOs) {
if (lSongDetailsJDO.getSongId().equals(pSongId)) {
lJDO = lSongDetailsJDO;
break;
}
}
return lJDO;
}
private void filterRecView(String pText) {
if (pText != null) {
if (pText.equals("")) {
mAdapter.swapData(mSongDetailsJDOs);
toggleVisibilityForNoResult(mSongDetailsJDOs.size(), pText);
} else {
ArrayList<SongDetailsJDO> lSongDetailsJDOs = new ArrayList<>();
pText = pText.toLowerCase();
for (SongDetailsJDO lDetailsJDO : mSongDetailsJDOs) {
if (lDetailsJDO.getTitle().toLowerCase().contains(pText) || lDetailsJDO.getAlbumName() != null && lDetailsJDO.getAlbumName().toLowerCase().contains(pText))
lSongDetailsJDOs.add(lDetailsJDO);
}
toggleVisibilityForNoResult(lSongDetailsJDOs.size(), pText);
mAdapter.swapData(lSongDetailsJDOs);
}
}
}
public void toggleVisibilityForNoResult(int pNumberOfSongs, String query) {
if (pNumberOfSongs == 0) {
mNoSongTV.setVisibility(View.VISIBLE);
mNoSongTV.setText(getString(R.string.nosong) + " " + query);
} else
mNoSongTV.setVisibility(View.INVISIBLE);
}
public void loadDataToRecyclerView() {
//Loading data to RecyclerView
mSongDetailsJDOs = new SongDetailTable(this).getAllSongs();
mAdapter = new SongListAdapter(SongsListActivity.this, mSongDetailsJDOs);
mRecyclerView.setAdapter(mAdapter);
}
public int getPositionOfSongId(String pSongId) {
int lPostion = -1;
for (int i = 0; i < mSongDetailsJDOs.size(); i++) {
if (mSongDetailsJDOs.get(i).getSongId().equals(pSongId)) {
lPostion = i;
break;
}
}
return lPostion;
}
}
Looking at your problem its not possible to tell exact cause of an issue.
But still i will give some hint over issue.
Check your code inside
mSongDetailsJDOs.size(); what is size of the list, it should be > 0. If not then check inside your activity how you are passing list.
#Override
public int getItemCount() {
if (mSongDetailsJDOs != null)
return mSongDetailsJDOs.size();
else
return 0;
}
If above list is > 0 then check it inside onBindViewHolder() that you are getting position one by one and try to render one item a time.
let me know if above works for you.
I am trying to create a "simple" image shared element transition between a recycler view item (Activity A) & a view pager (Activity B).
I have managed to smoothen everything but it seems that no matter how I set my exit/reenter/enter/return transitions the transition back from B to A, when the shared element is settled back, is flickering.
And by flickering I mean the entire screen.
I am using AppCompat themes & ActivityCompat methods with combinations of PreDrawListeners in both A (reenter) and B (when image is set using Glide).
Heres Activity A related code:
private Bundle mReenterState;
#TargetApi(Build.VERSION_CODES.LOLLIPOP) #Override protected void initActivityTransitions() {
super.initActivityTransitions();
getWindow().setSharedElementsUseOverlay(false);
setExitSharedElementCallback(mCallback);
}
private final SharedElementCallback mCallback = new SharedElementCallback() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP) #Override public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (mReenterState != null) {
int startingSelection = mReenterState.getInt(DefinesGlobal.KEY_STARTING_INDEX);
int currentSelection = mReenterState.getInt(DefinesGlobal.KEY_CURRENT_INDEX);
if (startingSelection != currentSelection) {
String newTransitionName = mHotel.getDetail().getHotelImagesGallery().get(currentSelection);
View newSharedElement = mContentView.getGalleryView().getRecyclerViewPager()
.getLayoutManager().findViewByPosition(currentSelection);
if (newSharedElement != null) {
names.clear();
names.add(newTransitionName);
sharedElements.clear();
sharedElements.put(newTransitionName, newSharedElement);
}
}
mReenterState = null;
} else {
// If mReenterState is null, then the activity is exiting.
View navigationBar = findViewById(android.R.id.navigationBarBackground);
View statusBar = findViewById(android.R.id.statusBarBackground);
if (navigationBar != null) {
names.add(navigationBar.getTransitionName());
sharedElements.put(navigationBar.getTransitionName(), navigationBar);
}
if (statusBar != null) {
names.add(statusBar.getTransitionName());
sharedElements.put(statusBar.getTransitionName(), statusBar);
}
}
}
};
#Override public void onActivityReenter(int resultCode, Intent data) {
super.onActivityReenter(resultCode, data);
mReenterState = new Bundle(data.getExtras());
int startingSelection = mReenterState.getInt(DefinesGlobal.KEY_STARTING_INDEX);
int currentSelection = mReenterState.getInt(DefinesGlobal.KEY_CURRENT_INDEX);
if (startingSelection != currentSelection) {
mContentView.getGalleryView().getRecyclerViewPager().scrollToPosition(currentSelection);
}
ActivityCompat.postponeEnterTransition(this);
mContentView.getGalleryView().getRecyclerViewPager().getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mContentView.getGalleryView().getRecyclerViewPager().getViewTreeObserver().removeOnPreDrawListener(this);
mContentView.getGalleryView().getRecyclerViewPager().requestLayout();
ActivityCompat.startPostponedEnterTransition(ViewModelHotelDetailsActivity.this);
return true;
}
});
}
Heres Activity B related code.
private boolean mIsReturning;
#Override public void finishAfterTransition() {
mIsReturning = true;
Intent data = new Intent();
data.putExtra(DefinesGlobal.KEY_CURRENT_INDEX, mCurrentSelection);
data.putExtra(DefinesGlobal.KEY_STARTING_INDEX, mStartingSelection);
setResult(RESULT_OK, data);
super.finishAfterTransition();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP) #Override protected void initActivityTransitions() {
super.initActivityTransitions();
ActivityCompat.postponeEnterTransition(this);
setEnterSharedElementCallback(mCallback);
}
private final SharedElementCallback mCallback = new SharedElementCallback() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP) #Override public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (mIsReturning) {
ViewModelFullSizeGalleryFragment fragment =
((FullSizeGalleryPagerAdapter) mPager.getAdapter()).getCurrent();
ImageView sharedElement = fragment.getImageView();
if (sharedElement == null) {
names.clear();
sharedElements.clear();
} else if (mStartingSelection != mCurrentSelection) {
names.clear();
names.add(sharedElement.getTransitionName());
sharedElements.clear();
sharedElements.put(sharedElement.getTransitionName(), sharedElement);
}
}
}
};
Activity B view pager fragment:
ublic class ViewModelFullSizeGalleryFragment extends ViewModelBaseFragment {
#Override protected int getLayoutId() {
return R.layout.view_model_fragment_full_size_gallery;
}
#Bind(R.id.full_size_image_view) ImageView mImageView;
public static ViewModelFullSizeGalleryFragment newInstance(String url, int position, int
target) {
ViewModelFullSizeGalleryFragment fragment = new ViewModelFullSizeGalleryFragment();
fragment.setUrl(url);
fragment.setIndexAndTarget(position, target);
return fragment;
}
private String mUrl;
private int mPosition;
private int mTarget;
public void setUrl(String url) {
mUrl = url;
}
public void setIndexAndTarget(int position, int target) {
mPosition = position;
mTarget = target;
}
#Override protected void initViewModel(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mImageView.setTransitionName(mUrl);
}
Glide.with(getContext())
.load(mUrl)
.transform(new HotelDetailsImageTransformation(getContext(),
LMTApplication.mWidth))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(true)
.error(R.drawable.ic_nopic)
.into(new SimpleTarget<GlideDrawable>() {
#Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
if (mImageView != null && resource != null) {
mImageView.setImageDrawable(resource);
mImageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override public boolean onPreDraw() {
mImageView.getViewTreeObserver().removeOnPreDrawListener(this);
if (mTarget == mPosition)
((ISharedElementCallback) getContext()).onViewReadyForTransition();
return true;
}
});
}
}
});
}
#Override public void onBackPressed() {
//!! Not used.
}
public interface ISharedElementCallback {
void onViewReadyForTransition();
}
public ImageView getImageView() {
return mImageView;
}
Havent figured out what actually does the flickering... Any help will be grand. Thanks.
Also. If its any help theres a map fragment behind the Activity A recycler view if its any help.
Does setting android:windowSharedElementsUseOverlay (inside your Activity's XML theme file) to false solve your problem?
am building an open source project, here is its link
http://slidese.github.io/slidese/2013/11/25/update_listview_item.html
I've imported all library projects which it is using into eclipse. All are fine except PullToRefresh library. it is giving me the error "PullToRefreshAttacher.OnRefreshListener cannot be resolved to a type" where fragment implements it
2nd error is at
mPullToRefreshAttacher.addRefreshableView(mListview, this);
it says "The method addRefreshableView(View, ViewDelegate) in the type PullToRefreshAttacher is not applicable for the arguments (ListView, ContentFragment)"
3rd error is at
#Override
public void onRefreshStarted(View view) {
Intent intent = new Intent(getActivity(), DownloaderService.class);
intent.putExtra(DownloaderService.EXTRA_USER_INITIATED, true);
getActivity().startService(intent);
}
It is asking me to remove override annotation. here is complete code of fragment.
public class ContentFragment extends Fragment implements PullToRefreshAttacher.OnRefreshListener {
private final String TAG = "ContentFragment";
public static final String CONTENT_MODE = "content_mode";
public static final int MODE_ADFREE = 0;
public static final int MODE_PREMIUM = 1;
private StartActivity mListener;
private PullToRefreshAttacher mPullToRefreshAttacher;
private UpdaterAsyncTask mUpdater;
private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
ListView mListview;
Button mPlayButton;
ContentAdapter mAdapter;
int mMode;
Map<String, UpdateHolder> mUpdates = new HashMap<String, UpdateHolder>();
public ContentFragment() {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (StartActivity) activity;
Log.d(TAG, "Attached podcast list fragment");
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must be the StartActivity");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_content, null);
mListview = (ListView) view.findViewById(android.R.id.list);
mListview.setEmptyView(view.findViewById(R.id.empty_list_view));
mListview.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
mListview.setMultiChoiceModeListener(new MultiChoiceModeListener() {
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.contextual_menu_content, menu);
return true;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.action_delete) {
SparseBooleanArray checked = mListview.getCheckedItemPositions();
Content[] params = new Content[checked.size()];
int index = 0;
int first = mListview.getFirstVisiblePosition();
int last = mListview.getLastVisiblePosition();
for (int i = 0; i < mListview.getCount(); i++) {
if (checked.get(i)) {
params[index++] = (Content)mListview.getItemAtPosition(i);
if (i >= first && i <= last) {
View view = mListview.getChildAt(i-first);
Animation animation = AnimationUtils.loadAnimation(getActivity(), android.R.anim.slide_out_right);
animation.setDuration(200);
//animation.setFillAfter(true);
animation.setStartOffset(100 * (index) );
view.startAnimation(animation);
}
}
}
new AsyncTask<Content, Void, Void>() {
#Override
protected Void doInBackground(Content... params) {
for (Content content : params) {
File file = Utils.getFilepath(content.getFilename());
file.delete();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mAdapter.notifyDataSetChanged();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
mode.finish();
return true;
}
return false;
}
#Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
}
});
mListview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
Content content = mAdapter.getItem(position);
mListener.showContentDetails(content);
}
});
mListview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mScrollState = scrollState;
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
mPullToRefreshAttacher = ((StartActivity) getActivity()).getPullToRefreshAttacher();
mPullToRefreshAttacher.addRefreshableView(mListview, this);
mMode = getArguments().getInt(CONTENT_MODE);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onPause() {
super.onPause();
if (mUpdater != null)
mUpdater.stop();
}
#Override
public void onResume() {
super.onResume();
updateAdapter();
mUpdater = new UpdaterAsyncTask();
mUpdater.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void)null);
}
public void refresh() {
updateAdapter();
}
public void replaceCurrentlyPlayingContent() {
GlobalContext.INSTANCE.replaceCurrentlyPLayingContent(mAdapter.getObjects(), mListener.getCurrentTrack());
}
private void updateAdapter() {
Log.d(TAG, "updateAdapter");
//new FetchContentAsyncTask(mMode).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
final String mp3 = mListener.getSavedStateMp3();
final boolean isPlaying = mListener.getSavedStateIsPlaying();
final boolean isPaused = mListener.getSavedStateIsPaused();
List<Content> listOfContent = GlobalContext.INSTANCE.getCachedContent(mMode, mp3, isPlaying, isPaused);
mAdapter = new ContentAdapter(getActivity(), R.layout.list_item_card, listOfContent);
mListview.setAdapter(mAdapter);
}
#Override
public void onRefreshStarted(View view) {
Intent intent = new Intent(getActivity(), DownloaderService.class);
intent.putExtra(DownloaderService.EXTRA_USER_INITIATED, true);
getActivity().startService(intent);
}
private class UpdaterAsyncTask extends AsyncTask<Void, Void, Void> {
boolean isRunning = true;
public void stop() {
isRunning = false;
}
#Override
protected Void doInBackground(Void... params) {
while (isRunning) {
/*
Map<String, UpdateHolder> map = gatherMetadata();
publishProgress(map);
*/
updateCurrentAdapterContent();
publishProgress();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onProgressUpdate(Void... params) {
super.onProgressUpdate();
if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
// http://stackoverflow.com/questions/2123083/android-listview-refresh-single-row
int start = mListview.getFirstVisiblePosition();
for(int i = start, j = mListview.getLastVisiblePosition(); i<=j; i++) {
View view = mListview.getChildAt(i-start);
if (((Content)mListview.getItemAtPosition(i)).dirty) {
Log.v(TAG, "Content is dirty");
mListview.getAdapter().getView(i, view, mListview);
}
}
}
}
}
private void updateCurrentAdapterContent() {
List<Content> listOfContent = mAdapter.getObjects();
Map<String, UpdateHolder> map = new HashMap<String, UpdateHolder>();
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_RUNNING);
try {
Cursor cursor = ContentDownloadManager.INSTANCE.query(q);
while (cursor.moveToNext()) {
//long id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
String uri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI));
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
int downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
int total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
float progress = (float)downloaded/(float)total;
UpdateHolder holder = new UpdateHolder();
holder.progress = progress;
holder.status = status;
map.put(uri, holder);
}
cursor.close();
final Content currentContent = mListener.getCurrentTrack();
final boolean isPlaying = mListener.isPlaying();
final boolean isPaused = mListener.isPaused();
for (Content content : listOfContent) {
// First update any download progress we might have for this specific content item
UpdateHolder holder = map.get(content.mp3);
if (holder != null) {
if (content.downloadProgress != holder.progress) {
content.downloadProgress = holder.progress;
content.dirty = true;
}
if (content.downloadStatus != holder.status) {
content.downloadStatus = holder.status;
content.dirty = true;
}
}
else {
if (content.downloadProgress != 0f) {
content.downloadProgress = 0f;
content.dirty = true;
}
if (content.downloadStatus != -1) {
content.downloadStatus = -1;
content.dirty = true;
}
}
// Update with elapsed (to be done)
// File exists?
File file = Utils.getFilepath(content.getFilename());
if (content.exists != file.exists()) {
content.exists = file.exists();
content.dirty = true;
}
// Is this the currently playing content
if (currentContent != null && content.mp3.equals(currentContent.mp3)) {
if (content.isPlaying != isPlaying) {
content.isPlaying = isPlaying;
content.dirty = true;
}
if (content.isPaused != isPaused) {
content.isPaused = isPaused;
content.dirty = true;
}
}
else {
if (content.isPlaying != false) {
content.isPlaying = false;
content.dirty = true;
}
if (content.isPaused != false) {
content.isPaused = false;
content.dirty = true;
}
}
if (content.dirty) {
DatabaseManager.getInstance().createOrUpdateContent(content);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public class UpdateHolder {
public String mp3;
public int status;
public boolean played;
public float progress;
public boolean exists = false;
public boolean isPlaying = false;
public boolean isPaused = false;
//public int elapsed;
//public int duration;
}
}
I couldn't find the issue in it. I'm stuck here for last 40 hours. Please help. Thank you!
Maybe you are using old version of the library. I found that PullToRefreshAttacher doesn't contain OnRefreshListener. (https://github.com/chrisbanes/ActionBar-PullToRefresh/blob/master/library/src/main/java/uk/co/senab/actionbarpulltorefresh/library/PullToRefreshAttacher.java)
Try to import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; and use it instead of PullToRefreshAttacher.OnRefreshListener.