I need to know how to deal with a syncadapter and paginated response from Rest Server. I'm making one android app that retrieves data collection. Each page comes with 20 items and I'm retrieving all my items in one request now. I think that the best way that I can do it is retrieve one page and when, for example, scrolling to the end of the list view making another request with the syncAdapter but I not sure.
I was searching how to deal in android with pagination in REST but I didn't find anything useful. I wanna know if someone can help me.
Thank you.
Here is an example that how I'm retrieving item now.
public ArrayList<ContentProviderOperation> parse(String json) throws IOException, NullPointerException {
final ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>();
AccountManager manager = AccountManager.get(mContext);
Account account = ((KipptApplication)mContext.getApplicationContext()).getCurrentAccount();
String authToken = manager.peekAuthToken(account, AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE);
Header[] headers = new Header[]{
new BasicHeader(KipptConstants.API_USERNAME_KEY,account.name),
new BasicHeader(KipptConstants.API_TOKEN_KEY,authToken)
};
Gson gson = new Gson();
Type responseType = new TypeToken<Response<ClipObject>>() {}.getType();
Response<ClipObject> inbox = gson.fromJson(json,responseType);
List<ClipObject> clips = inbox.getObjects();
String response = null;
String next = inbox.getMeta().getNext();
while(next !=null){
try {
Log.d(TAG,"Fetching more clips from: " + next);
response = HttpHelper.getHttpResponseAsString(KipptConstants.DOMAIN_URL +
next, null, headers);
inbox = gson.fromJson(response,responseType);
/*Updating next uri*/
next = inbox.getMeta().getNext();
if(!inbox.getObjects().isEmpty()){
clips.addAll(inbox.getObjects());
}else{
Log.d(TAG,"No more clips");
break;
}
} catch (PersonalizedException e) {
Log.e(TAG,e.toString());
e.printStackTrace();
}
}
for(ClipObject clip : clips){
if(mKipptDAO.isClipInDb(mContext.getContentResolver(),clip.getId(),true)== null){
Log.i(TAG,"Adding new clip");
/*Parsing clip*/
parseClip(clip,batch,false /*Clip isn't in database so update=false*/);
/*Parsing media*/
parseMedia(clip.getMedia(),clip.getId(),batch,false);
/*Parsing comments if clip contains it*/
if(clip.getCommentObjects().getCount()>0) {
List<CommentObject> comments = clip.getCommentObjects().getListElements();
for(CommentObject comment: comments){
parseComments(comment,clip.getId(),batch,false);
}
}
/*TODO Parse Likes*/
/*Parsing user creator*/
parseCreator(clip.getUserCreator(),batch,false);
}else{
Log.i(TAG,"Modifying clip");
/*Clip is in database*/
if(!(clip.getUpdated()<= timestamp)){
/*Parsing clip and update it in database*/
parseClip(clip,batch,true);
/*Parsing media and update it in database*/
parseMedia(clip.getMedia(),clip.getId(),batch,true);
/*Parsing comments and update it in database*/
if(clip.getCommentObjects().getCount()>0) {
List<CommentObject> comments = clip.getCommentObjects().getListElements();
for(CommentObject comment: comments){
parseComments(comment,clip.getId(),batch,true);
}
}
/*TODO parse likes*/
/*Parse Creator*/
parseCreator(clip.getUserCreator(),batch,true);
}
}
/*Updating timestamp*/
if(timestamp<=clip.getUpdated())timestamp = clip.getUpdated();
}
/*Saving timestamp modified value in preferences file*/
this.sharedPreferences.edit().putLong(KipptConstants.loadTimeStamp(currentFragmentIndex), timestamp).commit();
return batch;
}
for using SyncAdapter suggested you try this.
also for detect end of list in RecyclerView using this:
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// When went to the end of the list, load more posts
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (linearLayoutManager.findLastVisibleItemPosition() >= linearLayoutManager.getItemCount() - 1) {
// Grow List
}
}
}
Related
I have a wearable app. The app after it finishes has data like time/date, UUID, Geo location, parameters selected displayed in front of me like a Data Report or Log in several TextViews underneath each other. Like a list. I want this data to be transferred from my wearable device to my android phone.
Now I have to ask does the WearOS app the pairs the phone with the watch enables such a thing? Like can the data be sent through it? OR what exactly can I do? I read about Sync data items with the Data Layer API in the documentation, but I'm not sure if the code snippets provided would help achieve what I want.
public class MainActivity extends Activity {
private static final String COUNT_KEY = "com.example.key.count";
private DataClient dataClient;
private int count = 0;
...
// Create a data map and put data in it
private void increaseCounter() {
PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/count");
putDataMapReq.getDataMap().putInt(COUNT_KEY, count++);
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
Task<DataItem> putDataTask = dataClient.putDataItem(putDataReq);
}
...
}
The data I display in the textviews are called through methods that I call things like: getLocation, getUUID, getDateTime, getSelections, etc... when I click a button I call them in the setOnClickListener. I want this data in the TextViews to be placed in a file or something like that and send them over to the mobile phone from the watch when they're generated.
private void getDateTime()
{
SimpleDateFormat sdf_date = new SimpleDateFormat("dd/MM/yyyy");
SimpleDateFormat sdf_time = new SimpleDateFormat("HH:mm:ss z");
String currentDate= sdf_date.format(new Date());
String currentTime= sdf_time.format(new Date());
textView_date_time.setText("Date: "+currentDate+"\n"+"Time: "+currentTime);
}
#SuppressLint("SetTextI18n")
private void getUUID()
{
// Retrieving the value using its keys the file name
// must be same in both saving and retrieving the data
#SuppressLint("WrongConstant") SharedPreferences sh = getSharedPreferences("UUID_File", MODE_APPEND);
// The value will be default as empty string because for
// the very first time when the app is opened, there is nothing to show
String theUUID = sh.getString(PREF_UNIQUE_ID, uniqueID);
// We can then use the data
textView_UUID.setText("UUID: "+theUUID);
}
#SuppressLint("SetTextI18n")
private void getSelections()
{
textView_data_selected.setText("Tool No.: "+c.getToolNo()+
"\nTool Size: " +c.getToolSizeStr()+
"\nFrom Mode: " +c.getCurrentModeStr()+
"\nGoto Mode: " +c.getModeStr()+
"\nMethod: " +c.getMethodStr()+
"\nBit Duration: " +c.getBitDuration()+
"\nUpper bound" +c.getUpStageValue()+
"\nLower bound: "+c.getDownStageValue());
}
The above are examples of the methods I use to get the data. then I call them here:
gps_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Build.VERSION.SDK_INT >= 26) {
getLocation();
getDateTime();
getUUID();
getSelections();
}
else
{
//ActivityCompat.requestPermissions(get_location.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
Toast.makeText(get_location.this,"Build SDK too low",Toast.LENGTH_SHORT);
}
}
});
Now how do I take all this and send it over from my device to the the phone?
Note: The data report I want to send as a file, I want it done subtly like something done in the background. I don't know what else to do or where to look.
You have two options if you want to use the Data Layer, one is to use the MessageClient API to bundle your data up in a message and send it directly to the handheld. The easiest here would be to create an arbitrary JSONObject and serialize your data as a JSON string you can stuff into a message. For example:
try {
final JSONObject object = new JSONObject();
object.put("heart_rate", (int) event.values[0]);
object.put("timestamp", Instant.now().toString());
new MessageSender("/MessageChannel", object.toString(), getApplicationContext()).start();
} catch (JSONException e) {
Log.e(TAG, "Failed to create JSON object");
}
In my case, I do this in my onSensorChanged implementation, but you can insert this wherever you are updating your text.
MessageSender is just a threaded wrapper around the MessageClient:
import java.util.List;
class MessageSender extends Thread {
private static final String TAG = "MessageSender";
String path;
String message;
Context context;
MessageSender(String path, String message, Context context) {
this.path = path;
this.message = message;
this.context = context;
}
public void run() {
try {
Task<List<Node>> nodeListTask = Wearable.getNodeClient(context.getApplicationContext()).getConnectedNodes();
List<Node> nodes = Tasks.await(nodeListTask);
byte[] payload = message.getBytes();
for (Node node : nodes) {
String nodeId = node.getId();
Task<Integer> sendMessageTask = Wearable.getMessageClient(context).sendMessage(nodeId, this.path, payload);
try {
Tasks.await(sendMessageTask);
} catch (Exception exception) {
// TODO: Implement exception handling
Log.e(TAG, "Exception thrown");
}
}
} catch (Exception exception) {
Log.e(TAG, exception.getMessage());
}
}
}
The other option is to create a nested hierarchy of data items in the Data Layer and implement DataClient.OnDataChangedListener on both sides, such that changes that are written in on one side are automatically synchronized with the other. You can find a good walkthrough on how to do that here.
For your specific case, just packing it in a JSON object would probably be the simplest. The writing out to your preferred file format you can then implement on the handheld side without needing to involve the wear side.
I'm currently creating an app that needs to download a couple of videos then save the local path of it on a SQLite database.
At first, I wanted to get the URL of the video I downloaded but I can't seem to find anything that discusses about it. I tried to get COLUMN_MEDIAPROVIDER_URI and COLUMN_URI from the intent passed on the BroadcastReceiver for DownloadManager.ACTION_DOWNLOAD_COMPLETE but they return null.
Then I found about EXTRA_DOWNLOAD_ID. But if I use that, I still need to use something like a new HashMap that got the EXTRA_DOWNLOAD_ID of my download and the id of the video on my SQLite database for checking which is which.
I'm fine with that but I want to know if there's an easier way to do the thing I want.
I did this using OkHttp, as follows:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(YOUR_URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// ERROR MESSAGE
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
response.body().byteStream(); // byteStream with your result.
}
}
});
Another thing, maybe would be better if you store the videos on memory and just the address in your SQLite.
Using the code below from the SO question here
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
// get the DownloadManager instance
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Query q = new DownloadManager.Query();
Cursor c = manager.query(q);
if(c.moveToFirst()) {
do {
String name = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
Log.i("DOWNLOAD LISTENER", "file name: " + name);
} while (c.moveToNext());
} else {
Log.i("DOWNLOAD LISTENER", "empty cursor :(");
}
c.close();
}
}
and saving the download id on my ArrayList I was able to make a simpler way to check which download is finished.
I modified it to look like this for my use case.
Cursor c = dlMgr.query(new DownloadManager.Query());
boolean found = false;
if(c.moveToFirst()) {
do {
String dlFilePath = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
int dlId = Integer.parseInt( c.getString(c.getColumnIndex(DownloadManager.COLUMN_ID)) );
for(int x = 0; x < vidArrLst.size(); x++){
VideoAd va = vidArrLst.get(x);
if(va.getDownloadId() == dlId){
dbHelper.updateLocalPath(va.getVideoId(), dlFilePath);
va.setLocalPath(dlFilePath);
found = true;
break;
}
}
} while (c.moveToNext() && !found);
} else {
Log.d(TAG, "empty cursor :(");
}
UPDATE:
Sometimes this method will show that 2 downloads finished with the same file name which results to a video item to not have a local path. What I did is check if the local path is empty, download id is greater than 0, and if the download id is still downloading before playing a video so I can redownload a video and fix the gap and play the local file the next time the video needs to be played.
The situation is as following:
I send a login request using the method showLoginUI and then, after selecting a provider (Twitter, g+, etc.) the app goes to the onError callback of my GSLoginUIListener with the error "Account pending registration". Until that point, everything is fine. The problem is when I try to create another GSRequest with the method "accounts.setAccountInfo" like in the following code:
GSRequest setAccountInfoRequest = new GSRequest(getString(R.string.gigya_api_key),"accounts.setAccountInfo");
As parameter, I believe I have to add the regToken but where can I get it? In the iOS SDK, there is an Error object (that you get from the GSResponse that allows you to get it like this:
token = error.userInfo["regToken"]
But there is nothing like that on the Android SDK, from the GSResponse I just can get the error code, error message and error details. So, in short, how can I get the regToken that I need for my request? In the documentation does not go into the details of the actual implementation and I have not seen any examples.
Unlike the iOS and .NET SDKs, the Android SDK does not have a publicly expose or documented GSRequest class, so invoking a request the way you are doing it is not advisable.
Instead, you should use GSAPI.sendRequest with a GSResponseListener. The GSResponseListener will have a response object with the method getData which can be invoked to get a dictionary object of all the parameters returned from the request.
An example of how this can be done is provided in our Gigya CS Android demo hosted on GitHub and can be examined in the file SessionInfoFragment.java#121-191.
public void refreshView() {
GSAPI gigya = GSAPI.getInstance();
final TextView statusText = (TextView) rootView.findViewById(R.id.status_value);
final TextView nameText = (TextView) rootView.findViewById(R.id.name_value);
final TextView emailText = (TextView) rootView.findViewById(R.id.email_value);
final ImageView avatarView = (ImageView) rootView.findViewById(R.id.avatar);
if (gigya.getSession() != null){
if (gigya.getSession().isValid()) {
MainActivity parent = (MainActivity) getActivity();
GSObject user = parent.getUser();
// Retrieve the user if it's not set. (Reloaded app with active session)
if (user == null) {
GSResponseListener resListener = new GSResponseListener() {
#Override
public void onGSResponse(String method, GSResponse response, Object context) {
try {
if (response.getErrorCode()==0) { // SUCCESS! response status = OK
MainActivity parent = (MainActivity) getActivity();
Log.w("Gigya-Android-Demos", "Successfully set user");
parent.setUser(response.getData());
setLoggedIn(statusText, nameText, emailText, avatarView, response.getData());
} else { // Error
Log.w("Gigya-Android-Demos", "GSResponse: 'getAccountInfo' returned an error");
Log.w("Gigya-Android-Demos", response.getErrorMessage());
}
} catch (Exception ex) { ex.printStackTrace(); }
}
};
GSAPI.getInstance()
.sendRequest("accounts.getAccountInfo", null, resListener, null );
} else {
// Grab the user data
setLoggedIn(statusText, nameText, emailText, avatarView, user);
}
} else {
setLoggedOut(statusText, nameText, emailText, avatarView);
}
} else {
setLoggedOut(statusText, nameText, emailText, avatarView);
}
}
public void setLoggedOut(TextView status, TextView name, TextView email, ImageView avatar) {
status.setText(getString(R.string.logged_out));
name.setText(getString(R.string.null_value));
email.setText(getString(R.string.null_value));
setUnknownAvatar(avatar);
}
public void setLoggedIn(TextView status, TextView name, TextView emailView, ImageView avatar, GSObject user) {
status.setText(getString(R.string.logged_in));
try {
GSObject profile = user.getObject("profile");
String first = profile.getString("firstName");
String last = profile.getString("lastName");
String email = profile.getString("email");
if (profile.containsKey("photoURL")) {
setAvatar(avatar,profile.getString("photoURL"));
} else {
setUnknownAvatar(avatar);
}
name.setText(first + " " + last);
emailView.setText(email);
} catch (Exception ex) {
Log.w("Gigya-Android-Demos", "Something went horribly wrong with the user!");
ex.printStackTrace();
}
}
You should notice the use of getData() and GSObject classes throughout the example provided. Using this method of making a request, you should be able to examine the response data including the regToken.
I have been able to pull the Facebook newsfeed for the logged in user using the "me/home" Graph API Call and am displaying the result in an activity.
Now I have been trying numerous methods which will query the post's like column and check if the current user has liked a post to display the status. Essentially, I am setting a drawable to indicate the user has liked a post.
I cannot really post any code that I have tried so far simply because none of them work. And believe me, I have tried literally numerous methods.
I would appreciate if someone could at least prod me in the right direction.
EDIT: This is my latest attempt at querying the likes column and comparing the result with the current user's ID:
This code is where I am checking for likes count and adding them to an ArrayList. This is also where I am running the query to get the likes on the post.
// GET THE POST'S LIKES COUNT
if (json_data.has("likes")) {
JSONObject feedLikes = json_data.optJSONObject("likes");
String countLikes = feedLikes.getString("count");
postLikesCountArrayList.add(countLikes);
// TEST STARTS
// QUERY THE LIKES COLUMN TO CHECK YOUR LIKE STATUS ON A POST
Bundle params = new Bundle();
params.putString(Facebook.TOKEN, Utility.mFacebook.getAccessToken());
Utility.mAsyncRunner.request(finalThreadID + "/likes&limit=200", params, new LikesListener());
// TEST ENDS
} else {
String countLikes = "0";
postLikesCountArrayList.add(countLikes);
}
And this code block is the Listener (a privte class in the same activity) where the results are checked:
private class LikesListener extends BaseRequestListener {
#Override
public void onComplete(final String response, final Object state) {
try {
JSONObject JOLikes = new JSONObject(response);
JSONArray JALikes = JOLikes.getJSONArray("data");
for (int i = 0; i < JALikes.length(); i++) {
JSONObject JOTemp = JALikes.getJSONObject(i);
if (JOTemp.has("id")) {
String getTempID = JOTemp.getString("id");
if (getTempID.equals(initialUserID)) {
Runnable run = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
ImageView postYourLike = (ImageView) findViewById(R.id.postYourLike);
postYourLike.setBackgroundResource(R.drawable.btn_icon_liked);
}
};
TestNewsFeeds.this.runOnUiThread(run);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
You can use this fql. Replace the post_id
SELECT likes.can_like, likes.user_likes FROM stream WHERE post_id = "1274834235_3976149403543"
response would look like this, if user_likes is true, he has liked it
{
"data": [
{
"likes": {
"can_like": true,
"user_likes": false
}
}
]
}
I want to show Facebook Page's Notes items with those comments and likes using Graph API.
To do that, I'm using the asyncFacebookRunner in Facebook SDK.
Steps are like this:
call asyncFacebookRunner.request to get Note Item with PageId
mAsyncRunner.request(sAPIString, new NotesRequestListener(), null);
Response has come. ( I can't highlight function call. Sorry for inconvenient to find it.)
public class NotesRequestListener implements com.facebook.android.AsyncFacebookRunner.RequestListener
{
/**
* Called when the request to get notes items has been completed.
* Retrieve and parse and display the JSON stream.
*/
#Override
public void onComplete(String response, Object state) {
// TODO Auto-generated method stub
Log.i("My_TAG", "onComplete with response, state");
try
{
// process the response here: executed in background thread
final JSONObject json = new JSONObject(response);
JSONArray arrNotesItems = json.getJSONArray("data");
int l = (arrNotesItems != null ? arrNotesItems.length() : 0);
// !!!!
// This has another request call
// !!!!
final ArrayList<WordsItem> newItems = WordsItem.getWordsItems(arrNotesItems,getActivity());
WordsActivity.this.runOnUiThread(new Runnable() {
public void run() {
wordsItems.clear();
wordsItems.addAll(newItems);
aa.notifyDataSetChanged();
}
}); // runOnUiThread
} // try
catch (JSONException e)
{
Log.i("My_TAG", "JSON Error in response");
} // catch
} // onComplete
... other override methods ...
} // Request Listener
< Another Class >
public static ArrayList<WordsItem> getWordsItems(JSONArray arrJSON, Activity activity) {
ArrayList<WordsItem> wordsItems = new ArrayList<WordsItem>();
int l = (arrJSON != null ? arrJSON.length() : 0);
try {
WordsItem newItem;
for (int i=0; i<l; i++) {
JSONObject jsonObj = arrJSON.getJSONObject(i);
String sTitle = jsonObj.getString("subject");
String sNoteID = jsonObj.getString("id");
... get another fields here ...
newItem = new WordItem(...);
// !!!!
// This has request call for comments
// !!!!
ArrayList<CommentItem> arrComment = getUserComments(sNoteID);
wordsItems.add(newItem);
}
} catch (Exception e) {
e.printStackTrace();
}
return wordsItems;
} // getWordsItems
call another asyncFacebookRunner.request to get comments of item(with NoteID)
in getUserComments
mAsyncRunner.request(sAPIString, new CommentRequestListener(), null);
Before getting comments(OnComplete in CommentRequestListener has not called), getWordsItems returns item array.
So I can't see the comments.
How can I wait to update UI till getting comments?
(It's so ironic to synchronize asynchronized calls.)
Thanks in advance.
Use facebook object which has non-asynchronous request method.
You need not implement listener method.
So, I suggest below means.
use mAsyncRunner.request for first request call.
use mFacebookRunner.request for second request call.
I hope it may help you:-)
Using FQL - Facebook Query Language you can easily get all this information about any particular note info
, Also to get likes on that and comments over it as like examples given in the links.