Android IllegalStateException: Fragment not attached to Activity - android-fragments

I ran into this issue and came up with numerous posts both here and elsewhere related to this issue.
This application targets android 16, but min is set to 8. So I am using the Android Support Library to have access to newer apis.
I have an activity that loads a fragment which is a ListFragment. If you are running on a tablet in landscape mode, the first fragment, when list item is clicked, will load the second fragment in a 2 pane layout. If on a phone, a new activity loads the second fragment.
The second fragment uses a CursorLoader to access a ContentProvider as its backing data store. When this fragment is called from its own activity, there is no issue. However, when it is in 2 pane layout, the CursorLoader would fail with the above exception.
Here is the code:
public class ProgramGroupFragment extends MythtvListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = ProgramGroupFragment.class.getSimpleName();
private ProgramCursorAdapter adapter;
private String programGroup = "*";
public ProgramGroupFragment() { }
#Override
public Loader<Cursor> onCreateLoader( int id, Bundle args ) {
String[] projection = { BaseColumns._ID, ProgramConstants.FIELD_TITLE, ProgramConstants.FIELD_SUB_TITLE };
String[] selectionArgs = { programGroup };
CursorLoader cursorLoader = new CursorLoader( getActivity(), ProgramConstants.CONTENT_URI, projection, ProgramConstants.FIELD_TITLE + "=?", selectionArgs, ProgramConstants.FIELD_SUB_TITLE );
return cursorLoader;
}
#Override
public void onLoadFinished( Loader<Cursor> loader, Cursor cursor ) {
Log.v( TAG, "onLoadFinished : enter" );
adapter.swapCursor( cursor );
}
#Override
public void onLoaderReset( Loader<Cursor> loader ) {
adapter.swapCursor( null );
}
#Override
public void onActivityCreated( Bundle savedInstanceState ) {
super.onActivityCreated( savedInstanceState );
adapter = new ProgramCursorAdapter(
getActivity().getApplicationContext(), R.layout.program_row,
null, new String[] { ProgramConstants.FIELD_SUB_TITLE }, new int[] { R.id.program_sub_title },
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER );
setListAdapter( adapter );
getLoaderManager().initLoader( 0, null, this );
}
public void loadPrograms( String name ) {
programGroup = name;
try {
getLoaderManager().restartLoader( 0, null, this );
} catch( Exception e ) {
Log.w( TAG, e.getLocalizedMessage(), e );
}
}
#Override
public void onListItemClick( ListView l, View v, int position, long id ) {
super.onListItemClick( l, v, position, id );
Intent i = new Intent( getActivity(), VideoActivity.class );
i.putExtra( VideoActivity.EXTRA_PROGRAM_KEY, id );
startActivity( i );
}
private class ProgramCursorAdapter extends SimpleCursorAdapter {
public ProgramCursorAdapter( Context context, int layout, Cursor c, String[] from, int[] to, int flags ) {
super( context, layout, c, from, to, flags );
}
#Override
public View getView( int position, View convertView, ViewGroup parent ) {
View row = super.getView( position, convertView, parent );
getCursor().moveToPosition( position );
try {
int idIndex = getCursor().getColumnIndexOrThrow( BaseColumns._ID );
int titleIndex = getCursor().getColumnIndexOrThrow( ProgramConstants.FIELD_TITLE );
int subTitleIndex = getCursor().getColumnIndexOrThrow( ProgramConstants.FIELD_SUB_TITLE );
int id = getCursor().getInt( idIndex );
String title = getCursor().getString( titleIndex );
String subTitle = getCursor().getString( subTitleIndex );
if( row == null ) {
LayoutInflater inflater = getActivity().getLayoutInflater();
row = inflater.inflate( R.layout.program_row, parent, false );
}
TextView t = (TextView) row.findViewById( R.id.program_sub_title );
t.setText( !"".equals( subTitle ) ? subTitle : title );
} catch( Exception e ) {
Log.e( TAG, "getView : error", e );
}
return row;
}
}
}
The problem occurred in the method loadPrograms, specifically the line:
getLoaderManager().restartLoader( 0, null, this );
This was throwing the IllegalStateException. After looking through the documentation and numerous examples, I never once saw this issue and having to be caught in order to proceed.
I wrapped the code in a try/catch block just to see what would happen, and to my surprise, the app worked as expected.
EDIT:
Here is the Activity code that establishes the 2 pane layout:
public class RecordingsActivity extends AbstractRecordingsActivity implements RecordingsFragment.OnProgramGroupListener {
private static final String TAG = RecordingsActivity.class.getSimpleName();
private static final int REFRESH_ID = Menu.FIRST + 2;
private long requestId;
private BroadcastReceiver requestReceiver;
private DvrServiceHelper mDvrServiceHelper;
#Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_dvr_recordings );
RecordingsFragment recordingsFragment = (RecordingsFragment) getSupportFragmentManager().findFragmentById( R.id.fragment_dvr_program_groups );
recordingsFragment.setOnProgramGroupListener( this );
}
#TargetApi( 11 )
#Override
public boolean onCreateOptionsMenu( Menu menu ) {
Log.v( TAG, "onCreateOptionsMenu : enter" );
MenuItem refresh = menu.add( Menu.NONE, REFRESH_ID, Menu.NONE, "Refresh" );
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
refresh.setShowAsAction( MenuItem.SHOW_AS_ACTION_IF_ROOM );
}
return true;
}
/* (non-Javadoc)
* #see org.mythtv.client.ui.dvr.AbstractRecordingsActivity#onOptionsItemSelected(android.view.MenuItem)
*/
#Override
public boolean onOptionsItemSelected( MenuItem item ) {
switch( item.getItemId() ) {
case REFRESH_ID:
requestId = mDvrServiceHelper.getRecordingedList();
return true;
}
return super.onOptionsItemSelected( item );
}
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter( DvrServiceHelper.ACTION_REQUEST_RESULT );
requestReceiver = new BroadcastReceiver() {
#Override
public void onReceive( Context context, Intent intent ) {
long resultRequestId = intent.getLongExtra( DvrServiceHelper.EXTRA_REQUEST_ID, 0 );
if( resultRequestId == requestId ) {
int resultCode = intent.getIntExtra( DvrServiceHelper.EXTRA_RESULT_CODE, 0 );
if( resultCode == 200 ) {
Log.d( TAG, "Updating UI with new data" );
} else {
Log.d( TAG, "error occurred" );
}
} else {
Log.d( TAG, "Result is NOT for our request ID" );
}
}
};
mDvrServiceHelper = DvrServiceHelper.getInstance( this );
registerReceiver( requestReceiver, filter );
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onPause()
*/
#Override
public void onPause() {
super.onPause();
// Unregister for broadcast
if( null != requestReceiver ) {
try {
unregisterReceiver( requestReceiver );
requestReceiver = null;
} catch( IllegalArgumentException e ) {
Log.e( TAG, e.getLocalizedMessage(), e );
}
}
}
public void onProgramGroupSelected( String programGroup ) {
Log.d( TAG, "onProgramGroupSelected : enter" );
if( null != findViewById( R.id.fragment_dvr_program_group ) ) {
FragmentManager manager = getSupportFragmentManager();
ProgramGroupFragment programGroupFragment = (ProgramGroupFragment) manager.findFragmentById( R.id.fragment_dvr_program_group );
FragmentTransaction transaction = manager.beginTransaction();
if( null == programGroupFragment ) {
Log.v( TAG, "onProgramGroupSelected : creating new programGroupFragment" );
programGroupFragment = new ProgramGroupFragment();
transaction
.add( R.id.fragment_dvr_program_group, programGroupFragment )
.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN )
.addToBackStack( null )
.commit();
}
programGroupFragment.loadPrograms( programGroup );
} else {
Intent i = new Intent( this, ProgramGroupActivity.class );
i.putExtra( ProgramGroupActivity.EXTRA_PROGRAM_GROUP_KEY, programGroup );
startActivity( i );
}
}
}
The Listener method onProgramGroupSelected is where the single vs 2 pane layout is determined. The listener is called when the list item in the left pain is clicked.
Can anyone explain why this exception is occurring?
The full code for the project is located at https://github.com/MythTV-Android/mythtv-for-android

I experienced something similar. For me it seemed to help to avoid using the ApplicationContext when using a context. Try create your ProgramCursorAdapter without using the getApplicationContext().

Related

Change children pages data according to Tabbed page toolbar picker value change in Xamarin Forms

I have a Tabbed page in my Xamarin Forms app. There I have two children content pages. There are two pickers and a button in the toolbar of the Tabbed page. What I want is to populate data from the server according to the values, which is changing in the pickers. I am using the MVVM design pattern. How can I pass picker values to the children pages? I tried to use the MessagingCenter, It works sometimes but not all the time.
Tabbedpage
public partial class LifekpiRankingTabbedRM : ZeroTabbarHeight
{
public LifekpiRankingTabbedRM(string uRegion)
{
InitializeComponent();
BindingContext = new LifekpiRankingRmTabbedViewModel(uRegion);
Children.Add(new LifeKpiRankingViewRM(uRegion));
Children.Add(new LifeKpiRankingViewRMsBM(uRegion));
}
}
Tabbedpage VM
public class LifekpiRankingRmTabbedViewModel : BaseViewModelHelper
{
public MonthData2 SelectedMonth
{
get => _selectedMonth;
set
{
_selectedMonth = value;
GenerateData(_selectedMonth.mIndex, SelectedKpi).Await(CompletedTask, ErrorHandle);
OnPropertyChanged();
}
}
public string SelectedKpi
{
get => _selectedKpi;
set
{
_selectedKpi = value;
GenerateData(SelectedMonth.mIndex, _selectedKpi).Await(CompletedTask, ErrorHandle);
OnPropertyChanged();
}
}
public LifekpiRankingRmTabbedViewModel(string uRegion)
{
this.uRegion = uRegion;
SelectedMonth = GetCurrentMonth();
SelectedKpi = KpiList.First();
reportType = "Monthly";
ReportTypeTapCommand = new Command(ChangeTypeCommand);
}
private async Task GenerateData(int selectedMonth, string selectedKpi)
{
AcrDialogClass.ShowLoadingDialog("Loading...");
if (runCount > 2)
{
var resRmData = await _apiServices.GetLifeRegionalKpiWiseRanking(selectedMonth, selectedKpi, isCumulative);
var resBmForRmData = await _apiServices.GetLifeBranchKpiWiseRankingForRM(selectedMonth, selectedKpi, uRegion, isCumulative);
if (resRmData != null)
{
MessagingCenter.Send<App, string>(App.Current as App, "KPI_REGINAL_RANKING", resRmData);
}
if (resBmForRmData != null)
{
MessagingCenter.Send<App, string>(App.Current as App, "KPI_BM_RANKING_FOR_RM", resBmForRmData);
}
}
else
{
runCount++;
}
}
private MonthData2 GetCurrentMonth()
{
var months = Enumerable.Range(1, 12).Select(i => new { I = i, M = DateTimeFormatInfo.InvariantInfo.GetMonthName(i) });
foreach (var ss in months)
{
MonthList.Add(new MonthData2() { mIndex = ss.I, mName = ss.M });
}
return MonthList.Find(r => r.mIndex == dt.Month);
}
private void ChangeTypeCommand()
{
if (isCumulative)
{
reportType = "Monthly";
isCumulative = false;
}
else
{
reportType = "Cumulative";
isCumulative = true;
}
GenerateData(SelectedMonth.mIndex, SelectedKpi).Await(CompletedTask, ErrorHandle);
}
private void CompletedTask()
{
AcrDialogClass.HideLoadingDialog();
}
private void ErrorHandle(Exception ex)
{
AcrDialogClass.HideLoadingDialog();
}
}
Tab 1
public partial class LifeKpiRankingViewRM : ContentPage
{
private LifeKpiRankingViewModelRM vm;
public LifeKpiRankingViewRM(string uRegion)
{
InitializeComponent();
BindingContext = new LifeKpiRankingViewModelRM(uRegion);
vm = BindingContext as LifeKpiRankingViewModelRM;
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<App, string>(App.Current, "KPI_REGINAL_RANKING", (snd, arg) =>
{
vm.SetValues(arg);
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<App, string>(App.Current, "KPI_REGINAL_RANKING");
}
}
Tab 2
public partial class LifeKpiRankingViewRMsBM : ContentPage
{
private LifeKpiRankingViewModelBMForRM vm;
public LifeKpiRankingViewRMsBM(string uRegion)
{
InitializeComponent();
BindingContext = new LifeKpiRankingViewModelBMForRM(uRegion);
vm = BindingContext as LifeKpiRankingViewModelBMForRM;
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<App, string>(App.Current, "KPI_BM_RANKING_FOR_RM", (snd, arg) =>
{
vm.SetValues(arg);
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<App, string>(App.Current, "KPI_BM_RANKING_FOR_RM");
}
}
At first, you can try to get the child page's instance and then set its value.
If you set the tabbedpage in the app.cs with MainPage = new LifekpiRankingTabbedRM():
var tabbedpage = App.Current.MainPage as LifekpiRankingTabbedRM;
var childpage1 = tabbedpage.Children[0];
var vm1 = childpage1.BindingContext as LifeKpiRankingViewModelRM;
vm1.SetValues(...);
Generally speaking, when you have the instance of the TabbedPage, you can get its child page and update the child page's value.
In addition, you can show more details about the MessageCenter not work and we can help you go on using the MessageCenter.

In 3 level Nested Expand/ Collapse Vertical(all) Recyclerview data is not updated in inner Recyclerviews after 1st or subsequent time Qrcode scan data

After first qr code scan i make API call and get data. its work fine. But later when i scan other QRcode the data is retrieved but its not shown in inner recyclerview, mean no expand and collapse function works.
after instantiating recyclerview, adapter & layout manager. i scan qrcode and with result i make api call. then observe it and send data for first binding.
Below is Observer.
//ViewModel Observer
private void observeVIewModel() {
qrTrac_viewModel.getQRTrackListLiveData().observe(this, qrTrackingList -> {
if (qrTrac_viewModel.getSerResponseCodeVModel() != null) {
if (qrTrac_viewModel.getSerResponseCodeVModel().equals(Constants.OK)){
if (qrTrackingList != null) {
Util.Logd(qrTrackingList.getLstQRL0().toString());
progressActivity.dismiss();
Util.Logd("qrtrack viewmodel call");
mLstQRL0Adapter.setLstQRL0List((ArrayList<LstQRL0>) qrTrackingList.getLstQRL0(),(ArrayList<LstQRL1Info>) qrTrackingList.getLstQRL1Info(),
(ArrayList<LstQRTrackingSummary>) qrTrackingList.getLstQRTrackingSummary());
}
} else if(qrTrac_viewModel.getSerResponseCodeVModel().equals(Constants.INTERNAL_SERVER_ERROR)){
progressActivity.dismiss();
Util.Loge("in not found");
Snackbar snackbar = Snackbar.make(view, "Internal Server Error", BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, view -> {
});
snackbar.show();
}
else {
progressActivity.dismiss();
Snackbar snackbar = Snackbar.make(view, qrTrac_viewModel.getSerVModelResponseMsg(), BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> {});
snackbar.show();
}
}
else {
progressActivity.dismiss();
Snackbar snackbar = Snackbar.make(view, "Some Unknown Error occured", BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> {});
snackbar.show();
}
});
}
Here is first Adapter.
public class LstQRL0Adapter extends RecyclerView.Adapter<LstQRL0Adapter.LstQRL0ViewHolder> {
private Context context;
private List<LstQRL0> listQRL0 = new ArrayList<>();
private List<LstQRL1Info> listQRl1Info = new ArrayList<>();
private List<LstQRTrackingSummary> listQRTracSumm = new ArrayList<>();
private View view;
private LayoutInflater inflater;
private ArrayList<LstQRL1Info> templistQRl1Info = new ArrayList<>();
private int oldL0ClickPosition = -1;
//lstQRLInfo
private RecyclerView mLstQRL1InfoRecView;
private LstQRL1InfoAdapter mLstQRL1InfoAdapter;
private LinearLayoutManager mLst1Manager;
public LstQRL0Adapter(Context context) {
inflater = LayoutInflater.from(context);
this.context = context;
}
public void setLstQRL0List(ArrayList<LstQRL0> list, ArrayList<LstQRL1Info> listQRl1Info, ArrayList<LstQRTrackingSummary> listQRTracSumm) {
this.listQRL0 = list;
this.listQRl1Info = listQRl1Info;
this.listQRTracSumm = listQRTracSumm;
oldL0ClickPosition = -1 ;
notifyDataSetChanged();
#NonNull
#Override
public LstQRL0Adapter.LstQRL0ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
view = inflater.inflate(R.layout.lstqrl0_baseview, parent, false);
return new LstQRL0ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull LstQRL0Adapter.LstQRL0ViewHolder holder, int position) {
LstQRL0 myL0List = listQRL0.get(position);
holder.mSeason.setText(myL0List.getNvGDSeason());
holder.mStyle.setText(myL0List.getStyleBriefDescr().replace("<br/>", "\n"));
holder.mVendor.setText(myL0List.getNvPartnerVendor());
mLstQRL1InfoAdapter = new LstQRL1InfoAdapter(context);
mLstQRL1InfoRecView.setAdapter(mLstQRL1InfoAdapter);
mLstQRL1InfoRecView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
templistQRl1Info.clear();
for (int i = 0; i < listQRl1Info.size(); i++) {
if (listQRl1Info.get(position).getId().equals(listQRl1Info.get(i).getId()) && !listQRl1Info.get(i).getId().isEmpty()) {
Util.Logd(listQRl1Info.get(i).getId() + " is l0 adapter");
templistQRl1Info.add(listQRl1Info.get(i));
}
}
Util.Logd(templistQRl1Info.size() +" is temp l0 size");
mLstQRL1InfoAdapter.setLstQRL1infoList(templistQRl1Info, (ArrayList<LstQRTrackingSummary>) listQRTracSumm);
if (listQRL0.get(position).isL0Expanded()) {
holder.mLstQrL0ImageView.setImageResource(R.drawable.ic_arrow_drop_down);
mLstQRL1InfoRecView.setVisibility(View.VISIBLE);
} else {
holder.mLstQrL0ImageView.setImageResource(R.drawable.ic_arrow_right);
mLstQRL1InfoRecView.setVisibility(View.GONE);
}
view.setOnClickListener(view -> {
if (oldL0ClickPosition >= 0){
if (listQRL0.get(oldL0ClickPosition).isL0Expanded()) {
listQRL0.get(oldL0ClickPosition).setL0Expanded(false);
mLstQRL1InfoRecView.setVisibility(View.GONE);
notifyItemChanged(oldL0ClickPosition);
}
}
if (oldL0ClickPosition == holder.getAdapterPosition()){
oldL0ClickPosition = -1 ;
}else {
oldL0ClickPosition = holder.getAdapterPosition() ;
}
Util.Logd("Old clickc is "+oldL0ClickPosition);
if (oldL0ClickPosition >= 0){
new Handler(Looper.myLooper()).postDelayed(() -> {
listQRL0.get(oldL0ClickPosition).setL0Expanded(!listQRL0.get(oldL0ClickPosition).isL0Expanded());
notifyItemChanged(oldL0ClickPosition);
}, 500);
}
});
}
#Override
public int getItemCount() {
return listQRL0.size();
}
public class LstQRL0ViewHolder extends RecyclerView.ViewHolder {
private TextView mSeason, mStyle, mVendor;
private ImageView mLstQrL0ImageView;
public LstQRL0ViewHolder(#NonNull View itemView) {
super(itemView);
Util.Logd("just check is called");
mSeason = itemView.findViewById(R.id.lstqrl0_season);
mStyle = itemView.findViewById(R.id.lstqrl0_style);
mVendor = itemView.findViewById(R.id.lstqrl0_vendor);
mLstQrL0ImageView = itemView.findViewById(R.id.lstqr10_arrow);
mLstQRL1InfoRecView = itemView.findViewById(R.id.lstqrl1info_rv);
}
}
}
Here is Second Adapter
public class LstQRL1InfoAdapter extends RecyclerView.Adapter<LstQRL1InfoAdapter.LstQRL1InfoViewHolder> {
private Context context;
private List<LstQRL1Info> lstQRL1Infos = new ArrayList<>();
private List<LstQRTrackingSummary> listQRTracSumm = new ArrayList<>();
private ArrayList<LstQRTrackingSummary> tempListQRTracSumm = new ArrayList<>();
private View view;
private LayoutInflater inflater;
private int oldL1ClickPosition = 0; // not used in this case
//LstQRTracingSummary
private RecyclerView mQRLTracSummRV;
private LstQRTracSummAdapter mQrTracSummAdapter;
private LinearLayoutManager mQrTrackSummManager;
public LstQRL1InfoAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}
public void setLstQRL1infoList(ArrayList<LstQRL1Info> list, ArrayList<LstQRTrackingSummary> listQRTracSumm) {
this.lstQRL1Infos = list;
this.listQRTracSumm = listQRTracSumm;
notifyDataSetChanged();
}
#NonNull
#Override
public LstQRL1InfoViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
view = inflater.inflate(R.layout.lstqrl1info_baseview, parent, false);
return new LstQRL1InfoViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull LstQRL1InfoViewHolder holder, int position) {
holder.bind(lstQRL1Infos.get(position),position);
}
#Override
public int getItemCount() {
return lstQRL1Infos.size();
}
public class LstQRL1InfoViewHolder extends RecyclerView.ViewHolder {
private TextView quantity, info, add_info;
private ImageView mlstqrl1info_arrow;
public LstQRL1InfoViewHolder(#NonNull View itemView) {
super(itemView);
quantity = itemView.findViewById(R.id.lstqr1info_quantity);
info = itemView.findViewById(R.id.lstqr1info_info);
add_info = itemView.findViewById(R.id.lstqrl1info_add_info);
mQRLTracSummRV = itemView.findViewById(R.id.lstqrtracsumm_rv);
mlstqrl1info_arrow = itemView.findViewById(R.id.lstqr1info_arrow);
itemView.setOnClickListener(view -> {
if (lstQRL1Infos.get(oldL1ClickPosition).isL1Expanded()) {
Util.Logd("in expanded check");
lstQRL1Infos.get(oldL1ClickPosition).setL1Expanded(false);
mQRLTracSummRV.setVisibility(View.GONE);
notifyItemChanged(oldL1ClickPosition);
}
oldL1ClickPosition = getAdapterPosition() ;
new Handler(Looper.myLooper()).postDelayed(() -> {
}, 500);
lstQRL1Infos.get(getAdapterPosition()).setL1Expanded(!lstQRL1Infos.get(getAdapterPosition()).isL1Expanded());
notifyItemChanged(getAdapterPosition());
});
}
public void bind(LstQRL1Info lstQRL1Info,int position) {
quantity.setText(Math.round(lstQRL1Info.getmQty())+"");
info.setText(lstQRL1Info.getNvGDActivity());
add_info.setText(lstQRL1Info.getNvAdditionalInfo1());
mQrTracSummAdapter = new LstQRTracSummAdapter(context);
mQRLTracSummRV.setAdapter(mQrTracSummAdapter);
mQRLTracSummRV.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
tempListQRTracSumm.clear() ;
for (int i = 0; i < listQRTracSumm.size(); i++) {
if (lstQRL1Info.getId().equals(listQRTracSumm.get(i).getIdQRTrackingList()) && !listQRTracSumm.get(i).getIdQRTrackingList().isEmpty()) {
tempListQRTracSumm.add(listQRTracSumm.get(i));
Util.Logd(listQRTracSumm.get(i).getIdQRTrackingList() + " is info adapter");
}
}
Util.Logd("in view binding and size is " + lstQRL1Infos.size() + " "+ tempListQRTracSumm.size());
mQrTracSummAdapter.setListQRTracSumm(tempListQRTracSumm);
if (lstQRL1Infos.get(position).isL1Expanded()) {
mlstqrl1info_arrow.setImageResource(R.drawable.ic_arrow_drop_down);
mQRLTracSummRV.setVisibility(View.VISIBLE);
} else {
mlstqrl1info_arrow.setImageResource(R.drawable.ic_arrow_right);
mQRLTracSummRV.setVisibility(View.GONE);
}
}
}
}
Third adapter is also same as like second one.
First Adapter is bit different from second and third as am working on it to achieve what is needed.
Probelem : When i scan second and subsequent times then data from api call is received and i think properly set as well but somewhow its not show recyclerview 2 data.(First recyclerview data is shown properly).
Help me whether its different approach or update in same code.
Thanks in Advance.
UPDATE - Above code is correct , the mistake I was doing is -> I was creating RecyclerView adapter instance in advance when Activity runs for 1st time (Which is advance initialization of adapter) and was updating data later when QrCode is scanned and Api call is made .
So now I was creating instance when data is received by observer . as Shown Below
private void observeVIewModel() {
qrTrac_viewModel.getQRTrackListLiveData().observe(this, qrTrackingList -> {
if (qrTrac_viewModel.getSerResponseCodeVModel() != null) {
if (qrTrac_viewModel.getSerResponseCodeVModel().equals(Constants.OK)){
if (qrTrackingList != null) {
Util.Logd(qrTrackingList.getLstQRL0().toString());
progressActivity.dismiss();
Util.Logd("qrtrack viewmodel call");
// result sent to adapters for binding
mLstQRL0Adapter = new LstQRL0Adapter(this,(ArrayList<LstQRL0>) qrTrackingList.getLstQRL0(),(ArrayList<LstQRL1Info>) qrTrackingList.getLstQRL1Info(),
(ArrayList<LstQRTrackingSummary>) qrTrackingList.getLstQRTrackingSummary());
mLst0Manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mLst0RecView.setLayoutManager(mLst0Manager);
mLst0RecView.setNestedScrollingEnabled(false);
mLst0RecView.setRecycledViewPool(sharedPool);
mLst0RecView.setAdapter(mLstQRL0Adapter);
mLstQRL0Adapter.notifyDataSetChanged();
}
} else if(qrTrac_viewModel.getSerResponseCodeVModel().equals(Constants.INTERNAL_SERVER_ERROR)){
progressActivity.dismiss();
Util.Loge("in not found");
Snackbar snackbar = Snackbar.make(view, "Internal Server Error", BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, view -> {
});
snackbar.show();
}
else {
progressActivity.dismiss();
Snackbar snackbar = Snackbar.make(view, qrTrac_viewModel.getSerVModelResponseMsg(), BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> {});
snackbar.show();
}
}
else {
progressActivity.dismiss();
Snackbar snackbar = Snackbar.make(view, "Some Unknown Error occured", BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> {});
snackbar.show();
}
});
}
Same way I was doing for further adapters for nested lists to work better .
1st dapter.
public class LstQRL0Adapter extends RecyclerView.Adapter<LstQRL0Adapter.LstQRL0ViewHolder> {
private Context context;
private List<LstQRL0> listQRL0 = new ArrayList<>() ;
private List<LstQRL1Info> listQRl1Info = new ArrayList<>(); ;
private List<LstQRTrackingSummary> listQRTracSumm = new ArrayList<>();
private View view;
private LayoutInflater inflater;
private ArrayList<LstQRL1Info> templistQRl1Info = new ArrayList<>();
private int oldL0ClickPosition = -1;
private Boolean isFromClose = false ;
//lstQRLInfo
private RecyclerView mLstQRL1InfoRecView;
public LstQRL1InfoAdapter mLstQRL1InfoAdapter;
public LstQRL0Adapter(Context context,ArrayList<LstQRL0> list, ArrayList<LstQRL1Info> listQRl1Info, ArrayList<LstQRTrackingSummary> listQRTracSumm) {
inflater = LayoutInflater.from(context);
this.context = context;
this.listQRL0 = list;
this.listQRl1Info = listQRl1Info;
this.listQRTracSumm = listQRTracSumm;
oldL0ClickPosition = -1;
}
#NonNull
#Override
public LstQRL0Adapter.LstQRL0ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
view = inflater.inflate(R.layout.lstqrl0_baseview, parent, false);
return new LstQRL0ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull LstQRL0Adapter.LstQRL0ViewHolder holder, int position) {
LstQRL0 myL0List = listQRL0.get(position);
holder.mSeason.setText(myL0List.getNvGDSeason());
holder.mStyle.setText(myL0List.getStyleBriefDescr().replace("<br/>", "\n"));
holder.mVendor.setText(myL0List.getNvPartnerVendor());
Util.Logd( "temp l0 size is "+templistQRl1Info.size());
if (!isFromClose){
templistQRl1Info.clear();
if (oldL0ClickPosition >=0){
for (int i = 0; i < listQRl1Info.size(); i++) {
if (listQRL0.get(oldL0ClickPosition).getId().equals(listQRl1Info.get(i).getId())) {
Util.Logd( "temp ids are "+listQRl1Info.get(i).getId());
templistQRl1Info.add(listQRl1Info.get(i));
}
}
}
mLstQRL1InfoAdapter = new LstQRL1InfoAdapter(context,templistQRl1Info, (ArrayList<LstQRTrackingSummary>) listQRTracSumm);
mLstQRL1InfoRecView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
mLstQRL1InfoRecView.setNestedScrollingEnabled(false);
mLstQRL1InfoRecView.setAdapter(mLstQRL1InfoAdapter);
mLstQRL1InfoAdapter.notifyDataSetChanged();
}
boolean isL0Expanded = myL0List.isL0Expanded();
Util.Logd("Expanded status is "+ position + " " + listQRL0.get(position).isL0Expanded());
if (isL0Expanded) {
holder.mLstQrL0ImageView.setImageResource(R.drawable.ic_arrow_drop_down);
holder.hideShowL0Layout.setVisibility(View.VISIBLE);
} else {
holder.mLstQrL0ImageView.setImageResource(R.drawable.ic_arrow_right);
holder.hideShowL0Layout.setVisibility(View.GONE);
}
holder.itemView.setOnClickListener(view -> {
Util.Logd("////////////////////////");
if (oldL0ClickPosition >= 0) {
if (listQRL0.get(oldL0ClickPosition).isL0Expanded()) {
listQRL0.get(oldL0ClickPosition).setL0Expanded(false);
holder.hideShowL0Layout.setVisibility(View.GONE);
Util.Logd("Id to close is "+listQRL0.get(oldL0ClickPosition).getId());
isFromClose = true ;
notifyItemChanged(oldL0ClickPosition);
}
}
if (oldL0ClickPosition == holder.getAdapterPosition()) {
oldL0ClickPosition = -1;
} else {
oldL0ClickPosition = holder.getAdapterPosition();
}
Util.Logd("New click is " + oldL0ClickPosition);
if (oldL0ClickPosition >= 0 && oldL0ClickPosition != -1) {
new Handler(Looper.myLooper()).postDelayed(() -> {
listQRL0.get(oldL0ClickPosition).setL0Expanded(!listQRL0.get(oldL0ClickPosition).isL0Expanded());
Util.Logd("new data modify is "+listQRL0.get(oldL0ClickPosition).getId());
isFromClose = false ;
notifyItemChanged(oldL0ClickPosition);
},500);
}
});
}
#Override
public int getItemCount() {
return listQRL0.size();
}
public class LstQRL0ViewHolder extends RecyclerView.ViewHolder {
private TextView mSeason, mStyle, mVendor;
private ImageView mLstQrL0ImageView;
private LinearLayout hideShowL0Layout;
public LstQRL0ViewHolder(#NonNull View itemView) {
super(itemView);
mSeason = itemView.findViewById(R.id.lstqrl0_season);
mStyle = itemView.findViewById(R.id.lstqrl0_style);
mVendor = itemView.findViewById(R.id.lstqrl0_vendor);
mLstQrL0ImageView = itemView.findViewById(R.id.lstqr10_arrow);
mLstQRL1InfoRecView = itemView.findViewById(R.id.lstqrl1info_rv);
hideShowL0Layout = itemView.findViewById(R.id.hideShowL0Layout);
}
}
}
There is one more nested adapter but thats also same , so am not posting that here.
If need any help then just post comment.

Filter Data from RecyclerView Firebase

I am trying to implement a searchview in my action bar and for that to filter the RecyclerView Data that I retrieved from Firebase. Right now I am looking for the code that I need to add to the recycleradapter to be able to filter the retrieved data.
This is how I added the recyclerView to my MainActivity.
Query query = mRef.orderByChild("city");
// everything else
FirebaseRecyclerAdapter<City, CityViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<City, CityViewHolder>(
City.class,
R.layout.city_starter,
CityViewHolder.class,
query
) {
#Override
protected void populateViewHolder(CityViewHolder viewHolder, City city, int position) {
viewHolder.setDetails(getApplicationContext(),
city.getCity());
}
// for click of parent item
#Override
public CityViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CityViewHolder viewHolder = super.onCreateViewHolder(parent, viewType);
viewHolder.setOnClickListener(new CityViewHolder.ClickListener() {
#Override
public void onItemClick(View view, int position) {
//get Data from Firebase
String cCity = getItem(position).getCity();
// pass this data to new activity
Intent secondMainActivity = new Intent(view.getContext(), SecondMainActivity.class);
secondMainActivity.putExtra("city", cCity);
startActivity(secondMainActivity);
}
#Override
public void onItemLongClick(View view, int position) {
// possible to implement another way of interacting for a long click
}
});
return viewHolder;
}
};
// set adapter to recyclerview
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
Is there anybody who is able to help?
Thank you :)
This is what I found searching for solutions but I don't get it how to adapt it to my code:
public void filter(String text) {
items.clear();
if(text.isEmpty()){
items.addAll(itemsCopy);
} else{
text = text.toLowerCase();
for(PhoneBookItem item: itemsCopy){
if(item.name.toLowerCase().contains(text) || item.phone.toLowerCase().contains(text)){
items.add(item);
}
}
}
notifyDataSetChanged();
}
You can use android's Filterable interface..
Follow this link for example
Firstly implements Filterable to your adapter class.. You can use another array list of same type you are using for storing and showing filtered items.
Implements Filterable interface's method getFilter()
Example Code...
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
String charString = charSequence.toString();
if (charString.isEmpty()) {
contactListFiltered = contactList;
} else {
List<Contact> filteredList = new ArrayList<>();
for (Contact row : contactList) {
// name match condition. this might differ depending on your requirement
// here we are looking for name or phone number match
if (row.getName().toLowerCase().contains(charString.toLowerCase()) || row.getPhone().contains(charSequence)) {
filteredList.add(row);
}
}
contactListFiltered = filteredList;
}
FilterResults filterResults = new FilterResults();
filterResults.values = contactListFiltered;
return filterResults;
}
#Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
contactListFiltered = (ArrayList<Contact>) filterResults.values;
// refresh the list with filtered data
notifyDataSetChanged();
}
};
}
Add search view into your toolbar....
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
// Associate searchable configuration with the SearchView
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.action_search)
.getActionView();
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
searchView.setMaxWidth(Integer.MAX_VALUE);
// listening to search query text change
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
// filter recycler view when query submitted
mAdapter.getFilter().filter(query);
return false;
}
#Override
public boolean onQueryTextChange(String query) {
// filter recycler view when text is changed
mAdapter.getFilter().filter(query);
return false;
}
});
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_search) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
// close search view on back button pressed
if (!searchView.isIconified()) {
searchView.setIconified(true);
return;
}
super.onBackPressed();
}

android fragment reloading (onCreate) each time when back Button pressed

I am new in android using fragments in my Project. first time my fragment is creating then api called and get data load in fragment. here when i clicked at any item i replaced fragment by another fragment there also another api called and load data to fragment.
now here problem situation generated for me.
from here i back Button pressed.
fragment reloading same as first time creating but it should be show data as i left before going to next fragment.
so please provide me solution how i can get same data as i left means savedInstanceState data.
im my first fragment getCategory method call Api and get Data first time when i choose any category i replace fragment with another fragment but when i m returning same getCategory method recall perform same process as it first time.
fragment should not call api method again on backpressed it should show same category on this i clicked before.
my first fragment where calling api......
public class LandingFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private GridLayoutManager gridLayoutManager;
private static RecyclerView category_Grid;
private Fragment myFragment = null;
ProgressBar mProgressView;
View mLoginFormView;
private Category category;
private CategoryAdapter categoryAdapter;
private List<CategoryObject> rowListItem;
private String productId;
private OnFragmentInteractionListener mListener;
public LandingFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment LandingFragment.
*/
// TODO: Rename and change types and number of parameters
public static LandingFragment newInstance(String param1, String param2) {
LandingFragment fragment = new LandingFragment();
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) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v= inflater.inflate(R.layout.fragment_landing, container, false);
return v;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initViews(view);
RecyclerViewListeners();
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Activity 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 interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
private void initViews(final View v) {
mLoginFormView = (View)v.findViewById(R.id.mainView);
mProgressView = (ProgressBar)v.findViewById(R.id.login_progress);
category_Grid = (RecyclerView)v.findViewById(R.id.cat_grid);
category_Grid.setHasFixedSize(true);
gridLayoutManager = new GridLayoutManager(getActivity(), 3);
category_Grid.setLayoutManager(gridLayoutManager);
}
private void RecyclerViewListeners(){
category_Grid.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), category_Grid, new ItemClickListener(){
#Override
public void onClick(View view, int position) {
String entityId = rowListItem.get(position).getCategoryId();
String catName = rowListItem.get(position).getName();
Integer ishave = rowListItem.get(position).getIshaveSubcategories();
if(ishave==1) {
myFragment = SubcategoryFragment.newInstance(""+catName, "" + entityId);
ActivityUtils.launchFragementWithAnimation(myFragment, getActivity());
}else{
myFragment = ProductListFragment.newInstance("", "" + entityId);
ActivityUtils.launchFragementWithAnimation(myFragment, getActivity());
}
}
#Override
public void onLongClick(View view, int position) {
}
}));
}
public void getCategory() {
showProgress(true);
String URL = getResources().getString(R.string.category_api);
StringRequest req = new StringRequest(Request.Method.POST,URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
VolleyLog.v("Response:%n %s", response);
Gson gson = new GsonBuilder().serializeNulls().create();
try {
JSONObject jsonObject = new JSONObject(response);
if (jsonObject.getString("status").equals(getResources().getString(R.string.response_success))){
category = gson.fromJson(response, Category.class);
rowListItem = category.getCategory();
if(navigationUpdated){
someEventListener.someEvent(rowListItem);
navigationUpdated = false;
}
Log.d("CATEGORYID::::::::",""+rowListItem.get(1).getCategoryId());
categoryAdapter = new CategoryAdapter(getActivity(),rowListItem);
category_Grid.setAdapter(categoryAdapter);
categoryAdapter.notifyDataSetChanged();
return;
}
else if (jsonObject.getString("status").equals(getResources().getString(R.string.login_Er_respose))){
Log.e("","ERRORRRRRR");
return;
}
} catch (JSONException e) {
showProgress(false);
Log.e("My App", "Could not parse malformed JSON: \"" + response + "\"");
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
showProgress(false);
VolleyLog.e("Error: ", error.getMessage());
}
}){
#Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<String, String>();
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
params.put("Content-Type","application/x-www-form-urlencoded");
return params;
}
};
AppController.getInstance().addToRequestQueue(req);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
private void showProgress(final boolean show) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
mLoginFormView.animate().setDuration(shortAnimTime).alpha(
show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
});
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mProgressView.animate().setDuration(shortAnimTime).alpha(
show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
}
});
} else {
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
You can check your rowListItem.size(), if it's size is 0 then call getCategory() service, otherwise load your data from your rowListItem. Here is sample code which I am using to load data from arraylist if it is not empty:
if (mArrayArticle.size() == 0) {
isDataLoading = true;
mRecyclerList.setVisibility(View.INVISIBLE);
getCategory();
} else {
mHomeItemAdapter = new HomeItemAdapter(getActivity(), mArrayArticle, this);
mRecyclerList.setAdapter(mHomeItemAdapter);
}
Here mArrayArticle is my ArrayList, Hope it will help you.
for more clarification i want to tell..
how i implement the #Bhvk_AndroidBee solution
fragment backpressed call onActivityCreated Method so first overridethis method in fragment
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//here you can check null condition for rowListItem
}
}
inside onActivityCreated method I checked the condition like that
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(rowListItem!=null){
categoryAdapter = new CategoryAdapter(getActivity(),rowListItem);
category_Grid.setAdapter(categoryAdapter);
categoryAdapter.notifyDataSetChanged();
}else {
//call the method for first time creating your view
getCategory();
}
}
hope this would be helpfull for more strugglers like me...

Android: Prevent notfying Parent/Descendent uri's in ContentProvider while using CursorLoader?

I have a CursorLoader that observes a directory with this URI:
uriDirectory = content://com.myapp.stocks/stocks
and another CursorLoader that observes an item with this URI:
uriItem = content://com.myapp.stocks/stocks/GOOG
When I update uriItem and call getContext().getContentResolver().notifyChange(uriItem , null); in my ContentProvider, how can I prevent it from notifying uriDirectory as well?
Thanks!
Edit: So my solution so far is just to have a boolean that is set to true when I notify a uriItem. Then when it notifies the parent, uriDirectory, it will see that the boolean is true and won't perform any operations. After, I set the boolean back to false.
You can write your own CursorLoader. The default cursor loaders register a content observer via Cursor.RegisterContentObserver(ContentObserver observer). Instead, we want to use registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer).
I'm not sure if you are using the support library CursorLoader but for the greatest applicability, that's what I'm using.
The only changes from the stock android versions are in loadInBackground(). You should create an entire class instead of just extending android's and overriding loadInBackground because it protects you from future changes made to Android. Be advised that this will not use any notification url you set for the cursor in your ContentProvider unless you the device is KitKat or newer
Uri notificationUri;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
notificationUri = cursor.getNotificationUri();
} else {
notificationUri = mUri;
}
getContext().getContentResolver().registerContentObserver(
notificationUri != null ? notificationUri : mUri,
false, //don't notify for descendants
mObserver
);
Full class descendantChangeIgnoringCursorLoader.java:
package com.innomatixdata.busscan.utils;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.ContentResolverCompat;
import android.support.v4.content.Loader;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
public class DescendantChangeIgnoringCursorLoader extends AsyncTaskLoader<Cursor> {
final Loader.ForceLoadContentObserver mObserver;
Uri mUri;
String[] mProjection;
String mSelection;
String[] mSelectionArgs;
String mSortOrder;
Cursor mCursor;
android.support.v4.os.CancellationSignal mCancellationSignal;
/* Runs on a worker thread */
#Override
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new android.support.v4.os.OperationCanceledException();
}
mCancellationSignal = new android.support.v4.os.CancellationSignal();
}
try {
Cursor cursor = ContentResolverCompat.query(getContext().getContentResolver(),
mUri, mProjection, mSelection, mSelectionArgs, mSortOrder,
mCancellationSignal);
if (cursor != null) {
try {
// Ensure the cursor window is filled.
cursor.getCount();
Uri notificationUri;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
notificationUri = cursor.getNotificationUri();
} else {
notificationUri = mUri;
}
getContext().getContentResolver().registerContentObserver(
notificationUri != null ? notificationUri : mUri,
false, //don't notify for descendants
mObserver
);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}
#Override
public void cancelLoadInBackground() {
super.cancelLoadInBackground();
synchronized (this) {
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
}
}
}
/* Runs on the UI thread */
#Override
public void deliverResult(Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
/**
* Creates an empty unspecified CursorLoader. You must follow this with
* calls to {#link #setUri(Uri)}, {#link #setSelection(String)}, etc
* to specify the query to perform.
*/
public DescendantChangeIgnoringCursorLoader(Context context) {
super(context);
mObserver = new Loader.ForceLoadContentObserver();
}
/**
* Creates a fully-specified CursorLoader. See {#link ContentResolver#query(Uri, String[],
* String, String[], String) ContentResolver.query()} for documentation on the meaning of the
* parameters. These will be passed as-is to that call.
*/
public DescendantChangeIgnoringCursorLoader(Context context, Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
super(context);
mObserver = new Loader.ForceLoadContentObserver();
mUri = uri;
mProjection = projection;
mSelection = selection;
mSelectionArgs = selectionArgs;
mSortOrder = sortOrder;
}
/**
* Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
* will be called on the UI thread. If a previous load has been completed and is still valid
* the result may be passed to the callbacks immediately.
*
* Must be called from the UI thread
*/
#Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread
*/
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
public Uri getUri() {
return mUri;
}
public void setUri(Uri uri) {
mUri = uri;
}
public String[] getProjection() {
return mProjection;
}
public void setProjection(String[] projection) {
mProjection = projection;
}
public String getSelection() {
return mSelection;
}
public void setSelection(String selection) {
mSelection = selection;
}
public String[] getSelectionArgs() {
return mSelectionArgs;
}
public void setSelectionArgs(String[] selectionArgs) {
mSelectionArgs = selectionArgs;
}
public String getSortOrder() {
return mSortOrder;
}
public void setSortOrder(String sortOrder) {
mSortOrder = sortOrder;
}
#Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
writer.print(prefix); writer.print("mUri="); writer.println(mUri);
writer.print(prefix); writer.print("mProjection=");
writer.println(Arrays.toString(mProjection));
writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
writer.print(prefix); writer.print("mSelectionArgs=");
writer.println(Arrays.toString(mSelectionArgs));
writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
}
}

Resources