onRequestPermissionsResult() does not finish before onViewCreated() conduct - android-fragments

public class PdfRendererBasicFragment extends Fragment implements View.OnClickListener {
/**
* Key string for saving the state of current page index.
*/
private static final String STATE_CURRENT_PAGE_INDEX = "current_page_index";
public static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 2;
/**
* File descriptor of the PDF.
*/
/**
* {#link android.graphics.pdf.PdfRenderer} to render the PDF.
*/
private PdfRenderer mPdfRenderer;
/**
* Page that is currently shown on the screen.
*/
private PdfRenderer.Page mCurrentPage;
/**
* {#link android.widget.ImageView} that shows a PDF page as a {#link android.graphics.Bitmap}
*/
private ImageView mImageView;
/**
* {#link android.widget.Button} to move to the previous page.
*/
private Button mButtonPrevious;
/**
* {#link android.widget.Button} to move to the next page.
*/
private Button mButtonNext;
private Bundle copySavedInstanceState;
public PdfRendererBasicFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_pdf_renderer_basic, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Retain view references.
mImageView = (ImageView) view.findViewById(R.id.image);
mButtonPrevious = (Button) view.findViewById(R.id.previous);
mButtonNext = (Button) view.findViewById(R.id.next);
// Bind events.
mButtonPrevious.setOnClickListener(this);
mButtonNext.setOnClickListener(this);
mImageView.setOnClickListener(this);
copySavedInstanceState = savedInstanceState;
while (true) {
int rc = ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (rc == PackageManager.PERMISSION_GRANTED)
break;
}
// Show the first page by default.
int index = 0;
// If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
if (null != copySavedInstanceState) {
index = copySavedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, 0);
}
Log.d("check","index : " + index);
showPage(index);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
int rc = ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (rc == PackageManager.PERMISSION_GRANTED) {
try {
openRenderer(activity);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(activity, "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
activity.finish();
}
} else {
requestExternalStoragePermission();
}
}
#Override
public void onDetach() {
try {
closeRenderer();
} catch (IOException e) {
e.printStackTrace();
}
super.onDetach();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (null != mCurrentPage) {
outState.putInt(STATE_CURRENT_PAGE_INDEX, mCurrentPage.getIndex());
}
}
private void requestExternalStoragePermission() {
final String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (!ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(getActivity(), permissions, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
return;
}
final Activity thisActivity = getActivity();
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View view) {
ActivityCompat.requestPermissions(thisActivity, permissions,
EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
}
};
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
Log.d("TAG", "Got unexpected permission result: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("TAG", "Camera permission granted - initialize the camera source");
// we have permission, so can read SD Card now.
try {
openRenderer(getActivity());
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getActivity(), "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
getActivity().finish();
}
return;
}
Log.e("TAG", "Permission not granted: results len = " + grantResults.length +
" Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)"));
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
};
}
/**
* Sets up a {#link android.graphics.pdf.PdfRenderer} and related resources.
*/
private void openRenderer(Context context) throws IOException {
File file = new File("/sdcard/Download/test.pdf");
mPdfRenderer = new PdfRenderer(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
}
/**
* Closes the {#link android.graphics.pdf.PdfRenderer} and related resources.
*
* #throws java.io.IOException When the PDF file cannot be closed.
*/
private void closeRenderer() throws IOException {
if (null != mCurrentPage) {
mCurrentPage.close();
}
mPdfRenderer.close();
}
/**
* Shows the specified page of PDF to the screen.
*
* #param index The page index.
*/
private void showPage(int index) {
if (mPdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != mCurrentPage) {
mCurrentPage.close();
}
// Use `openPage` to open a specific page in PDF.
mCurrentPage = mPdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),
Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
// To render a portion of the page, use the second and third parameter. Pass nulls to get
// the default result.
// Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// We are ready to show the Bitmap to user.
mImageView.setImageBitmap(bitmap);
updateUi();
}
/**
* Updates the state of 2 control buttons in response to the current page index.
*/
private void updateUi() {
int index = mCurrentPage.getIndex();
int pageCount = mPdfRenderer.getPageCount();
mButtonPrevious.setEnabled(0 != index);
mButtonNext.setEnabled(index + 1 < pageCount);
getActivity().setTitle(getString(R.string.app_name_with_index, index + 1, pageCount));
}
/**
* Gets the number of pages in the PDF. This method is marked as public for testing.
*
* #return The number of pages.
*/
public int getPageCount() {
return mPdfRenderer.getPageCount();
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.previous: {
// Move to the previous page
showPage(mCurrentPage.getIndex() - 1);
break;
}
case R.id.next: {
// Move to the next page
Log.d("name", mCurrentPage.getIndex() + "");
showPage(mCurrentPage.getIndex() + 1);
break;
}
case R.id.image: {
Log.d("name", mCurrentPage.getIndex() + "");
break;
}
}
}
}
HI, this is my code.
I try to fix. but it's not going well.
problem is this.
When I conduct this app. Android request permission, and I Approve.
but at that time android perform 'onViewCreated(View view, Bundle savedInstanceState)' method before 'openRenderer(activity)' is done.
so 'showPage(index)' raise some error. and after then (I mean permission already approved, and execute again) app doing well.
but I want to fix that.
I think
At the first time, onAttach(Activity activity) execute
and after permission approved,
onAttach(Activity activity) and
onViewCreated(View view, Bundle savedInstanceState)
methods seems to run at the same time.
Please advice me.
Thank you.

#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Retain view references.
Log.d("timeStamp", "onViewCreated ");
mImageView = (ImageView) view.findViewById(R.id.image);
mButtonPrevious = (Button) view.findViewById(R.id.previous);
mButtonNext = (Button) view.findViewById(R.id.next);
// Bind events.
mButtonPrevious.setOnClickListener(this);
mButtonNext.setOnClickListener(this);
mImageView.setOnClickListener(this);
Log.d("timeStamp", "onViewCreated 할당완료");
while (true) {
int rc = getActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (rc == PackageManager.PERMISSION_GRANTED){
try {
openRenderer(getActivity());
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getActivity(), "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
getActivity().finish();
}
break;
}
Log.d("timeStamp", "얼마나 기다리는겨");
}
// Show the first page by default.
int index = 0;
// If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
if (null != savedInstanceState) {
index = savedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, 0);
}
Log.d("timeStamp", "showPage 호출 직전");
showPage(index);
}
}
I avoid this problem to add this code.
while (true) {
int rc = getActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (rc == PackageManager.PERMISSION_GRANTED){
try {
openRenderer(getActivity());
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getActivity(), "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
getActivity().finish();
}
break;
}
Log.d("timeStamp", "얼마나 기다리는겨");
}
but. this is not a best.
I figured this happen because onAttach method call requestPermissions method and end. onViewCreated start before User answer to request and onRequestPermissionsResult called (So openRenderer start).
So I suspend showPage method until PERMISSION_GRANTED.

Related

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);
}
}

Refresh list and adapter when DB changed in custom Adapter

my project is todo
i have 4 tab , 4 fragment with 4 list(Actionbar navigation contain 4 tab and ViewPager).
4 list(tab) use same db table but each of them retrieve different data with categoryID.
i use a Asynctask for all of them ,to read data and set adapter to list.
public class AsyncTaskDB extends AsyncTask<Void, Void, listAdapter> {
Context act;
int Categoryid;
ArrayList<memo> arraymemo;
listAdapter myadapter;
ListView list;
listAdapter listAdp;
public AsyncTaskDB(Context acti, int categoryID) {
this.act = acti;
this.Categoryid = categoryID;
}
#Override
protected listAdapter doInBackground(Void... params) {
MemoDBHelper helper = new MemoDBHelper(act);
// getAllDataByCategoryID
if (Categoryid != CategoryID.Done_ID)
arraymemo = helper.getAllTaskByCategory(Categoryid);
else
arraymemo = (ArrayList<memo>) helper.gatDoneMemo();
myadapter = new listAdapter(act, arraymemo);
if (myadapter == null) {
Toast.makeText(act, "no data", Toast.LENGTH_SHORT).show();
cancel(true);
}
return myadapter;
}
#Override
protected void onPostExecute(listAdapter result) {
switch (Categoryid) {
case CategoryID.Urgent_Imprtant_ID:
list = (ListView) ((Activity) act)
.findViewById(R.id.Urgent_Important_list);
break;
case CategoryID.Urgent_Less_Imprtant_ID:
list = (ListView) ((Activity) act)
.findViewById(R.id.Urgent_Less_Important_list);
break;
case CategoryID.Less_Urgent_Imprtant_ID:
list = (ListView) ((Activity) act)
.findViewById(R.id.Less_Urgent_Imprtant_list);
break;
case CategoryID.Neither_Urgent_Or_Imprtant_ID:
list = (ListView) ((Activity) act)
.findViewById(R.id.Neither_Urgent_Imprtant_list);
break;
case CategoryID.Done_ID:
list = (ListView) ((Activity) act).findViewById(R.id.ArchiveList);
break;
}
list.setAdapter(result);
this.listAdp = result;
}
public listAdapter getlistAdapter() {
return this.listAdp;
}
}
each memo in list have Done CheckBox.when user check and uncheck it,automatically memo update in db.(in custom adapter)
----------------------------
| -- |
| | | memotitle |
| -- |
----------------------------
public class listAdapter extends BaseAdapter implements OnCheckedChangeListener { Context act;
ArrayList<memo> MemoArray;
SparseBooleanArray mcheck;
int pos;
MemoDBHelper helper;
public listAdapter(Context activity, ArrayList<memo> memoarray) {
this.act = activity;
this.MemoArray = memoarray;
mcheck = new SparseBooleanArray(memoarray.size());
helper = new MemoDBHelper(act);
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return MemoArray.size();
}
#Override
public memo getItem(int position) {
// TODO Auto-generated method stub
return MemoArray.get(position);
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
public class viewHolder {
TextView title;
// TextView description;
CheckBox chkstatus;
}
viewHolder it;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
pos = position;
LayoutInflater in = ((Activity) act).getLayoutInflater();
if (convertView == null) {
convertView = in.inflate(R.layout.list_item, null);
it = new viewHolder();
it.title = (TextView) convertView.findViewById(R.id.txt_list_title);
it.chkstatus = (CheckBox) convertView
.findViewById(R.id.chkStatusid);
convertView.setTag(it);
} else {
it = (viewHolder) convertView.getTag();
}
it.title.setText(MemoArray.get(position).GetTitle());
it.chkstatus.setChecked(MemoArray.get(position).GetSattus());
it.chkstatus.setOnCheckedChangeListener(this);
it.chkstatus.setTag(String.valueOf(MemoArray.get(position).GetID()));
return convertView;
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mcheck.put(Integer.valueOf((String) buttonView.getTag()), isChecked);
helper.updateStatusByID(Integer.valueOf((String) buttonView.getTag()),
(isChecked));
helper.close();
//after db updatedt ,call method in fragment to notifydatsetchanged!
UrgentImportant_frg.notifyAdapter();
}
}
adapter must notify data changed ,and list don't show done memo.i don't how to do it !
my first fragment :
public class UrgentImportant_frg extends Fragment {
static listAdapter myadp;
ListView list;
// memo selectedmemo;
long[] checkid;
AsyncTaskDB asyn;
ArrayList<memo> selectedMemoArray;
final static int RQS_MoveTo = 10;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview = inflater.inflate(R.layout.urgentimportant_layout,
container, false);
return rootview;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
selectedMemoArray = new ArrayList<memo>();
list.setMultiChoiceModeListener(new MultiChoiceModeListener() {
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
// list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.list_select_menu, menu);
/*
* MenuInflater inflater = getActivity().getMenuInflater();
* inflater.inflate(R.menu.list_select_menu, menu);
*/
mode.setTitle("Select Items");
return true;
}
#Override
public boolean onActionItemClicked(final ActionMode mode,
MenuItem item) {
switch (item.getItemId()) {
case R.id.deletemenu:
final int[] myitemsid = getSelectedID();
final MemoDBHelper helper = new MemoDBHelper(getActivity());
AlertDialog.Builder myAlert = new AlertDialog.Builder(
getActivity());
myAlert.setMessage(
"Are you sure to delete " + myitemsid.length
+ " memo ?")
.setPositiveButton("yes", new OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
for (int j = 0; j < myitemsid.length; j++) {
helper.deleteRow(myitemsid[j]);
/*
* if (j == myitemsid.length - 1) {
* strid[j] = String
* .valueOf(myitemsid[j]); } else {
* strid[j] = String
* .valueOf(myitemsid[j]) + ","; }
*/
}
mode.finish();
onResume();
}
}).setNegativeButton("no", new OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
mode.finish();
}
});
AlertDialog alert = myAlert.create();
alert.show();
// mode.finish();
break;
case R.id.MoveTomenu:
// myadp.getItem(position);
Intent i = new Intent(getActivity(),
MoveToCategory_act.class);
i.putExtra("categoryid", CategoryID.Urgent_Imprtant_ID);
startActivityForResult(i, RQS_MoveTo);
mode.finish();
break;
}
return true;
}
// get selected id to delete and move category
#Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
if (myadp == null) {
myadp = asyn.getlistAdapter();
}
int p = ifMemoSelectedBefore(myadp.getItem(position));
if (p != -1) {
selectedMemoArray.remove(p);
} else if (checked) {
selectedMemoArray.add(myadp.getItem(position));
}
final int checkedCount = list.getCheckedItemCount();
switch (checkedCount) {
case 0:
mode.setSubtitle(null);
break;
case 1:
mode.setSubtitle("One Item Selected");
break;
default:
mode.setSubtitle(checkedCount + " Item Selected");
break;
}
}
});
getActivity().getActionBar().setSubtitle("subtitle");
}
public int ifMemoSelectedBefore(memo m) {
return selectedMemoArray.indexOf(m);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onViewCreated(view, savedInstanceState);
list = (ListView) view.findViewById(R.id.Urgent_Important_list);
// -------------click item
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
if (myadp == null) {
myadp = asyn.getlistAdapter();
}
// Log.d("tag", myadp.getItem(position).GetTitle() + "");
Intent nextintent = new Intent(getActivity(),
EditMemo_act.class);
memo g = myadp.getItem(position);
/*
* MemoDBHelper helper = new MemoDBHelper(getActivity());
* helper.updateStatusByID(g.GetID(), true);
*/
nextintent.putExtra("editmemo", g);
startActivity(nextintent);
}
});
}
#Override
public void onResume() {
asyn = new AsyncTaskDB(getActivity(), CategoryID.Urgent_Imprtant_ID);
asyn.execute();
super.onResume();
}
public int[] getSelectedID() {
int[] SelectedArray_ID = new int[selectedMemoArray.size()];
for (int j = 0; j < selectedMemoArray.size(); j++) {
SelectedArray_ID[j] = selectedMemoArray.get(j).GetID();
// Log.d("id", selectedMemoArray.get(j).GetID() + "");
}
return SelectedArray_ID;
}
//-------------a method to notifymyadpter
public static void notifyAdapter() {
if (myadp != null) {
myadp.notifyDataSetChanged();
Log.d("notify", "here");
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RQS_MoveTo) {
if (resultCode == Result.RESULT_OK) {
int id = data.getExtras().getInt("NEWCategoryID");
MemoDBHelper helper = new MemoDBHelper(getActivity());
final int[] myitemsid = getSelectedID();
for (int j = 0; j < myitemsid.length; j++) {
helper.updateCategory(myitemsid[j], id);
}
onResume();
}
}
}
}
is there any method in fragment to run after adapter changed?or in myadapter ,after db updated i call a method in fragment to notify data changed ? i think the second solution isn't right >_<
p.s:notifyAdapter() doesn't work,is it because my adapter fill in asyntask ?
When the adapter changed, try to call below method.
Once you use FragmentPagerAdapter or ListView, when you change the data, you should call this.
notifyDataSetChanged();
read about it : notifyDataSetChanged

Understanding FragmentStatePagerAdapter

I found this code online and am trying to test it out in my project. My project uses a database and I want to swipe between each record so I think ViewPager is the way to go. I have my activity and fragments already working but I have no clue how to implement the attached code into my project. What I'm looking for is where and how do I use this? From the activity or the fragment? and how do I go about doing it. I already have a cursor and the projection but now really sure how the two work together. I hope I'm making sense.
public class CursorPagerAdapter<F extends Fragment> extends FragmentStatePagerAdapter {
private final Class<F> fragmentClass;
private final String[] projection;
private Cursor cursor;
public CursorPagerAdapter(FragmentManager fm, Class<F> fragmentClass, String[] projection, Cursor cursor) {
super(fm);
this.fragmentClass = fragmentClass;
this.projection = projection;
this.cursor = cursor;
}
#Override
public F getItem(int position) {
if (cursor == null) // shouldn't happen
return null;
cursor.moveToPosition(position);
F frag;
try {
frag = fragmentClass.newInstance();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
Bundle args = new Bundle();
for (int i = 0; i < projection.length; ++i) {
args.putString(projection[i], cursor.getString(i));
}
frag.setArguments(args);
return frag;
}
#Override
public int getCount() {
if (cursor == null)
return 0;
else
return cursor.getCount();
}
public void swapCursor(Cursor c) {
if (cursor == c)
return;
this.cursor = c;
notifyDataSetChanged();
}
public Cursor getCursor() {
return cursor;
}
}
This is how you've to modify your code
ItemDetailFragment fragment = new ItemDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction().add(R.id.item_detail_container, fragment).commit();
ViewPager vpPager = (ViewPager) findViewById(R.id.pager);
CursorPagerAdapter<ItemDetailFragment> adapterViewPager = new CursorPagerAdapter(
getSupportFragmentManager(), fragment_class, your_projection, your_cursor);
vpPager.setAdapter(adapterViewPager);

Netty BufferSizePredictor truncates UDP datagram

I'm trying to develop custom Flume source which can receive custom UDP packets.
Here is my code:
public class XvlrUdpSource extends AbstractSource
implements EventDrivenSource, Configurable {
private static final Logger LOG = LoggerFactory.getLogger(XvlrUdpSource.class);
private int port;
private String host;
private Channel nettyChannel;
private static final Logger logger = LoggerFactory.getLogger(XvlrUdpSource.class);
private CounterGroup counterGroup = new CounterGroup();
public class XvlrUpdHander extends SimpleChannelHandler {
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent mEvent) {
try {
System.out.println("class: "+ mEvent.getMessage().getClass());
/** ChannelBuffer holds just first 768 bytes of the whole input UDP packet*/
ChannelBuffer channelBuffer = (ChannelBuffer)mEvent.getMessage();
Event xvlrPacketEvent = EventBuilder.withBody( ((ChannelBuffer)mEvent.getMessage()).array());
System.out.println("Length is:["+xvlrPacketEvent.getBody().length+"]");
//Event e = syslogUtils.extractEvent((ChannelBuffer)mEvent.getMessage());
if(xvlrPacketEvent == null){
return;
}
getChannelProcessor().processEvent(xvlrPacketEvent);
counterGroup.incrementAndGet("events.success");
} catch (ChannelException ex) {
counterGroup.incrementAndGet("events.dropped");
logger.error("Error writting to channel", ex);
return;
}
}
}
#Override
public void start() {
ConnectionlessBootstrap serverBootstrap = new ConnectionlessBootstrap
(new OioDatagramChannelFactory(Executors.newCachedThreadPool()));
final XvlrUpdHander handler = new XvlrUpdHander();
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() {
return Channels.pipeline(handler);
}
});
if (host == null) {
nettyChannel = serverBootstrap.bind(new InetSocketAddress(port));
} else {
nettyChannel = serverBootstrap.bind(new InetSocketAddress(host, port));
}
super.start();
}
#Override
public void stop() {
logger.info("Syslog UDP Source stopping...");
logger.info("Metrics:{}", counterGroup);
if (nettyChannel != null) {
nettyChannel.close();
try {
nettyChannel.getCloseFuture().await(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.warn("netty server stop interrupted", e);
} finally {
nettyChannel = null;
}
}
super.stop();
}
#Override
public void configure(Context context) {
Configurables.ensureRequiredNonNull(
context, "port");//SyslogSourceConfigurationConstants.CONFIG_PORT);
port = context.getInteger("port");//SyslogSourceConfigurationConstants.CONFIG_PORT);
host = context.getString("host");//SyslogSourceConfigurationConstants.CONFIG_HOST);
//formaterProp = context.getSubProperties("PROP");//SyslogSourceConfigurationConstants.CONFIG_FORMAT_PREFIX);
}
}
I did debug on messageRecieved and see in stacktrace that here:
/**
* Sends a {#code "messageReceived"} event to the first
* {#link ChannelUpstreamHandler} in the {#link ChannelPipeline} of
* the specified {#link Channel} belongs.
*
* #param message the received message
* #param remoteAddress the remote address where the received message
* came from
*/
public static void fireMessageReceived(Channel channel, Object message, SocketAddress remoteAddress) {
channel.getPipeline().sendUpstream(
new UpstreamMessageEvent(channel, message, remoteAddress));
}
My Object message is already 768 bytes length.
The root is here org.jboss.netty.channel.socket.oio.OioDatagramWorker:
byte[] buf = new byte[predictor.nextReceiveBufferSize()];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
Predictor sets buffer size to 768
Then:
fireMessageReceived(
channel,
channel.getConfig().getBufferFactory().getBuffer(buf, 0, packet.getLength()),
packet.getSocketAddress());
I do get only first 768 bytes.
is there any chance to change predictor behavior?
I've found this topic:
Netty Different Pipeline Per UDP Datagram
it's possible to "inject" predictor with desired behavior using special properties.
So full solution is:
public class XvlrUdpSource extends AbstractSource
implements EventDrivenSource, Configurable {
private static final Logger LOG = LoggerFactory.getLogger(XvlrUdpSource.class);
private int port;
private String host;
private Channel nettyChannel;
private CounterGroup counterGroup = new CounterGroup();
public class XvlrUpdHander extends SimpleChannelHandler {
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent mEvent) {
try {
ChannelBuffer channelBuffer = (ChannelBuffer)mEvent.getMessage();
int actualSizeOfUdpPacket = channelBuffer.readableBytes();
byte[] body = Arrays.copyOf(channelBuffer.array(), actualSizeOfUdpPacket);
Event xvlrPacketEvent = EventBuilder.withBody(body);
LOG.debug("Event.body length is: {} ", xvlrPacketEvent.getBody().length);
if(xvlrPacketEvent == null){
return;
}
getChannelProcessor().processEvent(xvlrPacketEvent);
counterGroup.incrementAndGet("events.success");
} catch (ChannelException ex) {
counterGroup.incrementAndGet("events.dropped");
LOG.error("Error writting to channel", ex);
return;
}
}
}
#Override
public void start() {
OioDatagramChannelFactory oioDatagramChannelFactory = new OioDatagramChannelFactory( Executors.newCachedThreadPool());
ConnectionlessBootstrap serverBootstrap = new ConnectionlessBootstrap(oioDatagramChannelFactory);
serverBootstrap.setOption("sendBufferSize", 65536);
serverBootstrap.setOption("receiveBufferSize", 65536);
serverBootstrap.setOption("receiveBufferSizePredictorFactory",
new AdaptiveReceiveBufferSizePredictorFactory(8192, 8192, 16384));
final XvlrUpdHander handler = new XvlrUpdHander();
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() {
return Channels.pipeline(handler);
}
});
if (host == null) {
nettyChannel = serverBootstrap.bind(new InetSocketAddress(port));
} else {
nettyChannel = serverBootstrap.bind(new InetSocketAddress(host, port));
}
}
#Override
public void stop() {
LOG.info("Syslog UDP Source stopping...");
LOG.info("Metrics:{}", counterGroup);
if (nettyChannel != null) {
nettyChannel.close();
try {
nettyChannel.getCloseFuture().await(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
LOG.warn("netty server stop interrupted", e);
} finally {
nettyChannel = null;
}
}
super.stop();
}
#Override
public void configure(Context context) {
Configurables.ensureRequiredNonNull(context, "port");
port = context.getInteger("port");
host = context.getString("host");
}
}
Either you are sending 768 bytes or the receiving buffer is only 768 bytes long. It certainly has nothing to do with carriage returns, unless there is some buggy handling of them in your code.

Resources