How parcelable works and why data should be in sequence? - java

I am using Parcelable. Here, i have done some test code.
TestParcelable.java
--------------------
package com.example.parcelableexample;
import android.os.Parcel;
import android.os.Parcelable;
public class TestParcelable implements Parcelable {
private String bName;
private String aName;
private int pTime;
public String getbName() {
return bName;
}
public void setbName(String bName) {
this.bName = bName;
}
public String getaName() {
return aName;
}
public void setaName(String aName) {
this.aName = aName;
}
public int getpTime() {
return pTime;
}
public void setpTime(int pTime) {
this.pTime = pTime;
}
public static final Parcelable.Creator<TestParcelable> CREATOR = new Creator<TestParcelable>() {
#Override
public TestParcelable createFromParcel(Parcel source) {
TestParcelable test = new TestParcelable();
test.bName = source.readString();
test.aName = source.readString();
test.pTime = source.readInt();
return test;
}
#Override
public TestParcelable[] newArray(int size) {
return new TestParcelable[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(bName);
dest.writeInt(pTime);
dest.writeString(aName);
}
}
MainActivity.java
-------------------
package com.example.parcelableexample;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
private Button btnPar;
public final static String PAR_KEY = "com.example.parcelableexample.par";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnPar = (Button) findViewById(R.id.button2);
btnPar.setOnClickListener(this);
}
public void parcelableMethod() {
TestParcelable testParcelable = new TestParcelable();
testParcelable.setbName("TOC");
testParcelable.setaName("Ullman");
testParcelable.setpTime(0000);
Intent intent = new Intent(this, ObjectP.class);
Bundle bundle = new Bundle();
bundle.putParcelable(PAR_KEY, testParcelable);
intent.putExtras(bundle);
startActivity(intent);
}
#Override
public void onClick(View v) {
if(v == btnSer) {
parcelableMethod();
}
}
When i am changing the sequence of write in this method writeToParcel(), than it is taking some null value instead of actual value. Some please explain why we should maintain the sequence of read and write data in parcelable?

I am not perfectly sure but according to my understanding both methods i.e. writeToParcel and createFromParcel are working like a pair, means you have to have same sequence in both of the methods.
Its working something like,
writeToParcel is generating a string in which its writing your first value i.e. bName(string value) than some seperator than pTime (int value) than again seperator and than aName (string value) while createFromParcel is converting from that string to object it is not getting the sequence as its expecting. Thats why it getting null value.
If you have to change sequence than you have to change on both ends.
note: above explanation is just for understanding , i am not truly aware of internal mechanism of these methods.

Related

How can I populate a ListView on a SecondActivity with data from FirstActivity in Android?

I'm working on a book-searching app project. The user enters a title, and the app searches Google Books for it.
I initially had one activity and layout. I decided to use two layouts (one for user to enter title, and the other displays results); had to create another activity because the results layout was throwing an exception about 'null' object reference.
After creating the second activity, I used an intent to transfer List data between the two activities; no sooner did I find out that I had to use a Serialize or Parcelable object for the purpose.
Following instructions at Pass list of objects from one activity to other activity in android & https://www.vogella.com/tutorials/AndroidParcelable/article.html, I failed at implementing both, because Serialize sent an empty ArrayList (thus, an empty results page, even though there're book hits) and Parcelable was throwing out different exceptions everytime I used it, probably because I'm using an ArrayAdapter to populate the ListView with books.
I don't think I'm ready to implement the API at https://www.vogella.com/tutorials/AutoValue/article.html and can't use Fragments as well. A better structure would be to use a single activity, View.GONE the background image I so much cherish, and display list of book objects under the search field. But it would be good to make a follow-up on Serialize & Parcelable -- for future projects.
So, fellow developers, what's the solution to this?
FirstActivity screenshot
SecondActivity screeenshot
Here's my code:
MainActivity:
package project.android.gbookslisting;
import android.app.LoaderManager;
import android.content.Intent;
import android.content.Loader;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import androidx.appcompat.app.AppCompatActivity;
import static project.android.gbookslisting.ResultsActivity.adapter;
//import android.support.v4.content.AsyncTaskLoader;
//import android.support.v7.app.AppCompatActivity;
public class ParamsActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Book>>, Serializable {
private static final int LOADER_ID = 0;
private static String LOG_TAG = ParamsActivity.class.getName();
EditText text;
String query;
private LoaderManager loaderManager = getLoaderManager();
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.parameters);
Button query = findViewById(R.id.search_button);
text = findViewById(R.id.deets_field);
query.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View view) {
if (text.getText().toString().length() > 0) { loaderManager.initLoader(LOADER_ID, null, ParamsActivity.this);
} else if (text.getText().length() < 1) {
text.setHint("Please enter book title/details");
text.setHintTextColor(Color.RED);
}
}
});
}
#Override
public Loader<List<Book>> onCreateLoader (int i, Bundle bundle) {
query = text.getText().toString();
return new BookLoader(this, query);
}
#Override
public void onLoadFinished (Loader<List<Book>> loader, List<Book> data) {
// If there is a valid list of {#link Book}s, then add them to the adapter's dataset. This will trigger the ListView to update.
if (data != null && !data.isEmpty()) {
data = new ArrayList<Book>();
Intent i = new Intent(getApplicationContext(), ResultsActivity.class);
i.putExtra("data", (Serializable) data);
startActivity(i);
}
}
#Override
public void onLoaderReset (Loader loader) {
adapter = new Adapt(this, new ArrayList<Book>());
adapter.clear();
}
}
SecondActivity:
package project.android.gbookslisting;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import java.io.Serializable;
import java.util.ArrayList;
import androidx.appcompat.app.AppCompatActivity;
public class ResultsActivity extends AppCompatActivity implements Serializable {
static Adapt adapter;
static TextView emptyResult;
ListView bookEntries;
String LOG_TAG = ResultsActivity.class.getName();
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hits_page);
Intent i = getIntent();
ArrayList<Book> books = (ArrayList<Book>) i.getSerializableExtra("data");
emptyResult = findViewById(R.id.matches_nill);
emptyResult.setText(R.string.matches0);
if (!books.isEmpty()) {
emptyResult.setVisibility(View.GONE);
// Create a new adapter that takes a rich (or otherwise empty) list of books as input
adapter = new Adapt(this, new ArrayList<Book>());
// Get the list of books from {#link Search}
bookEntries = findViewById(R.id.catalog);
bookEntries.setAdapter(adapter);
bookEntries.setEmptyView(emptyResult);
bookEntries.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick (AdapterView<?> adapterView, View view, int position, long l) {
// Find the current book that was clicked on
Book currentBook = adapter.getItem(position);
// Convert the String URL into a URI object (to pass into the Intent constructor)
Uri bookUri = Uri.parse(currentBook.getPage());
// Create a new intent to view the book URI
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, bookUri);
// Send the intent to launch a new activity
startActivity(websiteIntent);
}
});
// adapter.clear();
adapter.addAll(books);
} else {
emptyResult.setVisibility(View.VISIBLE);
emptyResult.setText(R.string.matches0);
}
}
}
Book obj.:
package project.android.gbookslisting;
import java.io.Serializable;
import java.util.Date;
public class Book {
private String book_title;
private String author;
private String publishing_year;
private String page;
public Book (String theTitle, String theAuthor, String theYear, String thePage) {
this.book_title = theTitle;
this.author = theAuthor;
this.publishing_year = theYear;
this.page = thePage;
}
public Book setBook_title (String book_title) {
this.book_title = book_title;
return this;
}
public Book setAuthor (String author) {
this.author = author;
return this;
}
public Book setPublishing_year (String publishing_year) {
this.publishing_year = publishing_year;
return this;
}
public Book setPage (String page) {
this.page = page;
return this;
}
protected String getAuthor () {
return author;
}
protected String getPublishing_year () { return publishing_year; }
protected String getPage () {
return page;
}
protected String getBook_title () {
return book_title;
}
}
You can declare the a variable that will handle your data = new ArrayList<Book>(); as public static.. However it's not recommended for high level codebase but for the structures of yours, you can implement it.
just declare a variable like this above protected void onCreate() in FirstActivity ..
public static List<Book> book_data = new ArrayList<>();
and transfer the data from public void onLoadFinished (Loader<List<Book>> loader, List<Book> data) to book_data
#Override
public void onLoadFinished (Loader<List<Book>> loader, List<Book> data) {
// If there is a valid list of {#link Book}s, then add them to the adapter's dataset. This will trigger the ListView to update.
if (data != null && !data.isEmpty()) {
//data = new ArrayList<Book>();
book_data = data;
Intent i = new Intent(getApplicationContext(), ResultsActivity.class);
startActivity(i);
}
}`
You can also try to debug it if it contains a value
Log.d(TAG, String.valueOf(data.size()));
and in SecondActivity
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hits_page);
List<Book> books = new ArrayList<>();
books = FirstActivity.book_data; //Get the data from the First Activity
....
....
}
All right, so I was able to correct my implementation of the Parcelable interface and the FirstActivity now passes data to SecondActivity. This is it:
package project.android.gbookslisting;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private String book_title;
private String author;
private String publishing_year;
private String page;
public Book (String theTitle, String theAuthor, String theYear, String thePage) {
this.book_title = theTitle;
this.author = theAuthor;
this.publishing_year = theYear;
this.page = thePage;
}
public Book(Parcel in) {
this.book_title = in.readString();
this.author = in.readString();
this.publishing_year = in.readString();
this.page = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
#Override
public Book createFromParcel (Parcel in) {
return new Book(in);
}
#Override
public Book[] newArray (int size) {
return new Book[size];
}
};
protected String getAuthor () { return author; }
protected String getPublishing_year () { return publishing_year; }
protected String getPage () {
return page;
}
protected String getBook_title () {
return book_title;
}
#Override
public int describeContents () {
return 0;
}
#Override
public void writeToParcel (Parcel parcel, int flags) {
parcel.writeString(book_title);
parcel.writeString(author);
parcel.writeString(publishing_year);
parcel.writeString(page);
}
}
I've shared the repository on GitHub #GBooks Listing, pending more features/updates.
ResultsActivity
Sure looks lame, but I'll improve the theming & whatever affects appearance. Thanks.

Get elements of ArrayList from retrieved ArrayList via intent via Parcelable in 2nd activity

I'm trying to build a simple android that should simulate learning vocabulary with cards. to get to the final app, I'm going to add functionality and complexity step by step, by learning new things, and adding new things.
I'm about to tell you where I'm stuck, but first, here's what my App should do, so far (I'm still far, far way from where I'd like it to go, but you don't have to mind about that.):
At this point the app should do the folowing thing:
1) in the MainActivity:
a) Create an Array of 3 instances of an implementation of the
Parcelable interface (class VocCard implements Parcelable), VocCard[]
voc1, in this case. Since the class VocCard implements Parcelable, a
Parcel is obtained for the construction of the 3 instances.
b) Create an ArrayList of the type VocCard called vocCardList and add
all 3 elements of voc1 to vocCardList.
c) Create an instance of a start button which creates an intent for
starting a 2nd activity called PracticeActivity when clicked.
d) Add the ArrayList vocCardList with Parcelable to the
intent.
2) in PracticeActivity
a) Get the intent created by MainActivity.
b) Retrieve ArrayList vocCardList from intent
c) Get any element of vocCardsList and assign a variable of the type
VocCard to it.
d) Retrieve a value of the assigned Voccard instance by invoking its
methods.
e) Display that value by setting a TextView to the value's String
value.
f) Create a Button nextButton which creates an intent for starting
the 2nd activity PracticeActivity again, as some kind of recursion.
g) Add the ArrayList vocCardList with parcelable to intent.
h) repeat 2) a)-g) until App is closed by closing-icon.
I'm currently stuck at 2) c), insofar that the App only works as described above for the index 0. Only VocCard card0 = vocCardList1.get(0); works, vocCardList1.get(1), or vocCardList1.get(2); don't, despite 1 and 2 being within the ArrayList boundries.
Oddly enough, the Runtime Exeption Message for using index 1 and index 2 is not the same:
with vocCardList1.get(1): java.lang.ClassCastException: java.lang.String cannot be cast to com.undiclosed.smartcards.VocCard
with vocCardList1.get(2): java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.undisclosed.smartcards.VocCard.returnVocForeign()' on a null object reference
Question:
Why can't I acces the elements of the ArrayList the way I expected? When I searched the web I was probably looking for the wrong stuff.
MainActivity.java:
package com.undisclosed123.smartcards;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<VocCard> vocCardList;
private String[] voc_f = {"bread","apple","water"};
private String[] voc_n = {"pain","pomme","eau"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Create VocCards and add to List
vocCardList = new ArrayList<VocCard>();
VocCard[] voc1 = new VocCard[3];
Parcel in = Parcel.obtain();
for(int i = 0; i < 3; i++){
voc1[i] = new VocCard(in);
voc1[i].setVocForeign(voc_f[i]);
voc1[i].setVocNative(voc_n[i]);
vocCardList.add(voc1[i]);
}
// Create Intent and assign the parcelable List for sending to second activity on btn click
Button startBtn = (Button) findViewById(R.id.button);
startBtn.setOnClickListener(new View.OnClickListener() {
#Override
#SuppressWarnings("unchecked")
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, PracticeActivity.class);
intent.putParcelableArrayListExtra("voc1",(ArrayList)vocCardList);
getApplicationContext().startActivity(intent);
}
});
}
}
And below, PracticeActivity.java:
(Sorry for the large sections which are commented out, I figured it could help communicating my further intentions for that class)
package com.undisclosed123.smartcards;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.ArrayList;
public class PracticeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_practice);
// Get the Intent that started this activity and extract the string
Intent intent = getIntent();
final ArrayList<VocCard> vocCardList1 = intent.getParcelableArrayListExtra("voc1"); //
//Get the Data from the VocCards
//VocCard card4count = vocCardList1.get(2);
// card4count.increaseCount();
//int count = card4count.getCount();
/* if(count >= vocCardList1.size()){
// TODO
//Create new intent for EndPracticeActivity
//makeshift statement
count--;
}*/
VocCard card0 = vocCardList1.get(2);
// VocCard card1 = vocCardList1.get(1);
String test1 = card0.returnVocForeign();
// card0.increaseCount();
// String test1 = "test1";
//Make a TextView display the transfered String
TextView textView = findViewById(R.id.textView);
textView.setText(test1);
//Create another intent that recalls same activity recursively
Button nextBtn = (Button) findViewById(R.id.button2);
nextBtn.setOnClickListener(new View.OnClickListener() {
#Override
#SuppressWarnings("unchecked")
public void onClick(View v) {
Intent intent = new Intent(PracticeActivity.this, PracticeActivity.class);
intent.putParcelableArrayListExtra("voc1",(ArrayList)vocCardList1);
getApplicationContext().startActivity(intent);
}
}); /**/
}
}
And at last, VocCard.java:
package com.undisclosed123.smartcards;
import android.os.Parcel;
import android.os.Parcelable;
public class VocCard implements Parcelable {
private String voc_foreign;
private String voc_native;
private boolean learned;
private int error_level;
private static int counter;
public String returnVocForeign(){
return voc_foreign;
}
public void setVocForeign(String voc_f){
voc_foreign = voc_f;
}
public String returnVocNative(){
return voc_native;
}
public void setVocNative(String voc_n){
voc_native = voc_n;
}
public boolean checkLearned(){
return learned;
}
public int getErrorLevel(){
return error_level;
}
public void makeLearned(){
learned = true;
}
public void increaseErrorLevel(){
error_level++;
}
public int getCount(){
return counter;
}
public void increaseCount(){
counter++;
}
public VocCard(Parcel in) {
voc_foreign = in.readString();
voc_native = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(voc_foreign);
dest.writeString(voc_native);
dest.writeInt((Boolean) learned ? 1 : 0);
dest.writeInt(error_level);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<VocCard> CREATOR = new Creator<VocCard>() {
#Override
public VocCard createFromParcel(Parcel in) {
return new VocCard(in);
}
#Override
public VocCard[] newArray(int size) {
return new VocCard[size];
}
};
}
The problem is with writing data to Parcel and reading data from it.
public class VocCard implements Parcelable {
...
...
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(voc_foreign);
dest.writeString(voc_native);
dest.writeInt(learned ? 1 : 0);
dest.writeInt(error_level);
}
/**
* This constructor is invoked by the method
* createFromParcel(Parcel source) of the object CREATOR.
*
* The order and number of writing and reading data to and from
* Parcel should be same
**/
private VocCard(Parcel in) {
voc_foreign = in.readString();
voc_native = in.readString();
learned = in.readInt() == 1;
error_level = in.readInt();
}
/**
* A constructor that initializes the VocCard object.
**/
VocCard(String voc_foreign, String voc_native) {
this.voc_foreign = voc_foreign;
this.voc_native = voc_native;
}
...
...
}
Few changes in MainActivity inside onCreate
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
...
...
//Create VocCards and add to List
mVocCardList = new ArrayList<>(3);
for (int i = 0; i < 3; i++) {
mVocCardList.add(new VocCard(voc_f[i], voc_n[i]));
}
Button startBtn = (Button) findViewById(R.id.button);
startBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, PracticeActivity.class);
intent.putParcelableArrayListExtra("voc1", (ArrayList<? extends Parcelable>) mVocCardList);
startActivity(intent);
}
});
}
Now get the VocCard list in PracticeActivity
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
...
...
final ArrayList<VocCard> vocCardList = getIntent().getParcelableArrayListExtra("voc1");
final VocCard card = vocCardList.get(2);
String test = card.getVocForeign();
...
...
}

Sorting data in List by specific value

I have an issue where my recyclerview adds new items at the end, whereas I want the latest items at the top upon user refresh. I've been scratching around all over the net and it essentially boils down to adding setreverseLayout or setStackFromEnd. This gives other complications such as the recycler view not scrolling to the top to the latest item.
I then had a thought of maybe ordering my data list by a specific value and then it should return it as I want it. Can this be done and would it resolve my issue? I want to sort it by value adopt_rownum desc.
My Custom Adapter
package com.example.admin.paws;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.List;
/**
* Created by admin on 9/16/2016.
*/
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
public Context context;
public List<MyData> my_data;
public CustomAdapter(Context context, List<MyData> my_data) {
this.context = context;
this.my_data = my_data;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardnew,parent,false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String name = my_data.get(position).getName();
String age = my_data.get(position).getAge();
String gender = my_data.get(position).getGender();
String SubstAge = age.substring(0,age.indexOf("(") -1);
String NameAgeGender = name + ", " + SubstAge + ", " + gender;
holder.about.setText(my_data.get(position).getAbout());
holder.NameAgeGender.setText(NameAgeGender);
Glide.with(context).load(my_data.get(position).getPhoto_path()).into(holder.photo_path);
//activity_card_details vars
// final String about = my_data.get(position).getAbout();
final String adoptId = my_data.get(position).getId()+"";
final String photo_path_dtls = my_data.get(position).getPhoto_path();
final String listedDate = my_data.get(position).getDatetime_listed();
final String status = my_data.get(position).getStatus();
final String breed = my_data.get(position).getBreed();
final String source = my_data.get(position).getSource();
final String contact_info = my_data.get(position).getContact_info();
final String suburb = my_data.get(position).getSuburb();
final String city = my_data.get(position).getCity();
final String province = my_data.get(position).getProvince();
final String concat_location = suburb + ", " + city + ", " + province;
final String viewCounter = my_data.get(position).getViewCounter()+"";
//When click on photo
holder.photo_path.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context,CardDetailsActivity.class);
intent.putExtra("listedDate",listedDate);
intent.putExtra("adoptId",adoptId);
// intent.putExtra("about",about);
intent.putExtra("status",status);
intent.putExtra("breed",breed);
intent.putExtra("source",source);
intent.putExtra("contactinfo",contact_info);
intent.putExtra("location",concat_location);
intent.putExtra("photo_path_dtls",photo_path_dtls);
intent.putExtra("viewCounter",viewCounter);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return my_data.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
public TextView NameAgeGender;
public ImageView photo_path;
public TextView about;
private ViewHolder(View itemView) {
super(itemView);
about = (TextView) itemView.findViewById(R.id.about);
NameAgeGender = (TextView) itemView.findViewById(R.id.tvNameAgeGender);
// NameAgeGender.setTextColor(Color.parseColor("#9C9393"));
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "fonts/Lato-Medium.ttf");
NameAgeGender.setTypeface(typeface);
//NameAgeGender.setBackgroundColor(Color.parseColor("#F35959"));
photo_path = (ImageView) itemView.findViewById(R.id.photo_path);
}
}
}
My DataList
package com.example.admin.paws;
public class MyData {
//the must be in the same order of the select column order
public int adopt_rownum
,viewCounter
,adopt_id;
private String
name
,type
,breed
,age
,gender
,size
,about
,photo_path
,source
,contact_info
,suburb
,city
,province
,datetime_listed
,status;
public MyData(
int adopt_rownum,
int viewCounter,
int adopt_id,
String name,
String type,
String breed,
String age,
String gender,
String size,
String about,
String photo_path,
String source,
String contact_info,
String suburb,
String city,
String province,
String datetime_listed,
String status)
{
this.adopt_rownum = adopt_rownum;
this.viewCounter = viewCounter;
this.adopt_id = adopt_id;
this.name = name;
this.type = type;
this.breed = breed;
this.age = age;
this.gender = gender;
this.size = size;
this.about = about;
this.photo_path = photo_path;
this.source = source;
this.contact_info = contact_info;
this.suburb = suburb;
this.city = city;
this.province = province;
this.datetime_listed = datetime_listed;
this.status = status;
}
//adopt_rownum used for filtering the records.
public int getAdopt_rownum() {
return adopt_rownum;
}
//viewcounter
public int getViewCounter() {
return viewCounter;
}
//adopt_id
public int getId() {
return adopt_id;
}
public void setId(int adopt_id) {
this.adopt_id = adopt_id;
}
//name
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//type
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
//breed
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
//age
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
//gender
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.age = gender;
}
//size
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
//about
public String getAbout() {
return about;
}
public void setAbout(String about) {
this.about = about;
}
//photo path
public String getPhoto_path() {
return photo_path;
}
public void setPhoto_path(String photo_path) {
this.photo_path = photo_path;
}
//source
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
//contact_info
public String getContact_info() {
return contact_info;
}
public void setContact_info(String contact_info) {
this.contact_info = contact_info;
}
//suburb
public String getSuburb() {
return suburb;
}
public void setSuburb(String suburb) {
this.contact_info = suburb;
}
//city
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
//province
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
//datetime_listed
public String getDatetime_listed() {
return datetime_listed;
}
public void setDatetime_listed(String datetime_listed) {
this.datetime_listed = datetime_listed;
}
//status
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
The fragment that displays the results. Another question on the side, inside this fragment for load_data_from_server its giving me a warning that "This Async task should be static or leaks might occur". I have no idea what this means since I'm completely new to JAVA.
package com.example.admin.paws;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* A simple {#link Fragment} subclass.
* Activities that contain this fragment must implement the
* {#link feedFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {#link feedFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class feedFragment 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";
public Activity FragActivity;
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private List<MyData> data_list;
private CustomAdapter adapter;
RecyclerView recyclerView;
public String EndOfFeed;
private OnFragmentInteractionListener mListener;
public feedFragment() {
// 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 fragment_feed.
*/
// TODO: Rename and change types and number of parameters
public static feedFragment newInstance(String param1, String param2) {
feedFragment fragment = new feedFragment();
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) {
setHasOptionsMenu(true);
View rootView = inflater.inflate(R.layout.fragment_feed, container, false);
final GridLayoutManager gridLayoutManager;
final SwipeRefreshLayout swipeRefreshLayout = rootView.findViewById(R.id.feedRefresh);
TextView tvEndOfFeed = rootView.findViewById(R.id.tvEndOfFeed);
tvEndOfFeed.setText(EndOfFeed);
//recycler view
recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
data_list = new ArrayList<>();
load_data_from_server(0, "getFeed.php");
gridLayoutManager = new GridLayoutManager(getActivity(), 1); //2 nr of cards next to each other
recyclerView.setLayoutManager(gridLayoutManager);
//gridLayoutManager.setReverseLayout(true);
adapter = new CustomAdapter(getActivity(), data_list);
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (gridLayoutManager.findLastCompletelyVisibleItemPosition() == data_list.size() - 1) {
load_data_from_server(data_list.get(data_list.size() - 1).getAdopt_rownum(), "getFeed.php");
}
}
});
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
//this works but issue with the ordering of the adoptrownum
load_data_from_server(data_list.get(data_list.size() -1).getAdopt_rownum(), "refreshFeed.php");
swipeRefreshLayout.setRefreshing(false);
}
});
return rootView;
}
private void load_data_from_server(final int adopt_id, final String phpScript) {
AsyncTask<Integer,Void,Void> task = new AsyncTask<Integer, Void, Void>() {
#Override
protected Void doInBackground(Integer... integers) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2/app_scripts/"+phpScript+"?adopt_rownum="+integers[0])
.build();
try {
Response response = client.newCall(request).execute();
JSONArray array = new JSONArray(response.body().string());
for (int i=0; i<array.length(); i++){
JSONObject object = array.getJSONObject(i);
MyData data = new MyData(
object.getInt("ADOPT_ROWNUM"),
object.getInt("VIEWCOUNTER"),
object.getInt("ADOPT_ID"),
object.getString("NAME"),
object.getString("TYPE"),
object.getString("BREED"),
object.getString("AGE"),
object.getString("GENDER"),
object.getString("SIZE"),
object.getString("ABOUT"),
object.getString("PHOTO_PATH"),
object.getString("SOURCE"),
object.getString("CONTACT_INFO"),
object.getString("SUBURB"),
object.getString("CITY"),
object.getString("PROVINCE"),
object.getString("DATETIME_LISTED"),
object.getString("STATUS")
);
data_list.add(data);
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
System.out.println("End of content"+e);
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
adapter.notifyDataSetChanged();
}
};
task.execute(adopt_id);
}
// 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(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This 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);
}
}
I added the Comparator as instructed below but the output is a bit weird...
public class MyComparator implements Comparator<MyData > {
#Override
public int compare(final MyData o1, final MyData o2) {
Log.d("APP", "compare Starting... ");
Integer val1 = o1.getAdopt_rownum();
Log.d("APP", "compare val1... "+val1);
Integer val2 = o2.getAdopt_rownum();
Log.d("APP", "compare val1... "+val2);
Log.d("APP", "compare val1 and val2 ="+val1.compareTo(val2));
return val1.compareTo(val2);
}
}
output
compare Starting...
compare val1... 2
compare val2... 1
compare val1 and val2 =1
...
compare Starting...
compare val1... 14
compare val2... 15
compare val2 and val2 =1
Below is how i implemented it in my fragment
private void load_data_from_server(final int adopt_id, final String phpScript) {
AsyncTask<Integer,Void,Void> task = new AsyncTask<Integer, Void, Void>() {
#Override
protected Void doInBackground(Integer... integers) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2/app_scripts/"+phpScript+"?adopt_rownum="+integers[0])
.build();
try {
Response response = client.newCall(request).execute();
JSONArray array = new JSONArray(response.body().string());
for (int i=0; i<array.length(); i++){
JSONObject object = array.getJSONObject(i);
MyData data = new MyData(
object.getInt("ADOPT_ROWNUM"),
object.getInt("VIEWCOUNTER"),
object.getInt("ADOPT_ID"),
object.getString("NAME"),
object.getString("TYPE"),
object.getString("BREED"),
object.getString("AGE"),
object.getString("GENDER"),
object.getString("SIZE"),
object.getString("ABOUT"),
object.getString("PHOTO_PATH"),
object.getString("SOURCE"),
object.getString("CONTACT_INFO"),
object.getString("SUBURB"),
object.getString("CITY"),
object.getString("PROVINCE"),
object.getString("DATETIME_LISTED"),
object.getString("STATUS")
);
data_list.add(data);
**Collections.sort(data_list,new MyComparator());**
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
//System.out.println("End of content"+e);
EndOfFeed = e+"";
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
adapter.notifyDataSetChanged();
}
};
task.execute(adopt_id);
}
You can sort list by a property using the following code:
Collections.sort(myList, new MyComparator());
public static class MyComparator implements Comparator<MyData > {
#Override
public int compare(final MyData o1, final MyData o2) {
return o1.getAdoptRownum().compareTo(o2.getAdoptRownum());
}
}
I usually declare comparators inside the relevant POJO class. Then you can call Collections.sort whenever you refresh your data.

ADK toolkit Android+Arduino store variable issue

I'm writing a program which is supposed to run "forever". The program is Android application for tablet which exchanges data with Arduino. I have already implemented the code for Arduino and Android, and it exchanges data very well. However, after 2 cycles of work, my instance of AdkManager becomes NULL. As I've read before, Android will null variables from time to time because it has limited resources. However here's the problem - the AdkManager has confirmed bug that once it has been closed, it can't be reopened. Thus I can't re-initiate the AdkManager instance and I need to store it somehow. So far I've been using Application extension. The code is below:
MyApplication:
package org.udoo.androidadkdemobidirect;
import android.app.Application;
import android.content.Context;
import android.hardware.usb.UsbManager;
import me.palazzetti.adktoolkit.AdkManager;
/**
* Created by admin on 8/18/16.
*/
public class MyApplication extends Application {
private String someVariable;
public String getSomeVariable() {
return someVariable;
}
public void setSomeVariable(String someVariable) {
this.someVariable = someVariable;
}
public static class sAdkManager{
private static sAdkManager ourInstance = null;
public static sAdkManager getInstance() {
if (ourInstance==null)
ourInstance = new sAdkManager();
return ourInstance;
}
private static AdkManager mAdkManager = null;
public void write(String s){
mAdkManager.writeSerial(s);
}
public String read(){
return mAdkManager.readSerial();
}
public void open(){
mAdkManager.open();
}
public void close(){
mAdkManager.close();
}
public boolean checkNull(){
return mAdkManager==null;
}
public static void init(Context context){
if(mAdkManager==null) {
mAdkManager = new AdkManager((UsbManager) context.getSystemService(Context.USB_SERVICE));
context.registerReceiver(mAdkManager.getUsbReceiver(), mAdkManager.getDetachedFilter());
}
}
private sAdkManager() {
}
}
}
MainActivity:
package org.udoo.androidadkdemobidirect;
import me.palazzetti.adktoolkit.AdkManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.TextView;
import android.widget.ToggleButton;
//import org.udoo.androidadkdemobidirect.sAdkManager;
public class MainActivity extends Activity{
// private static final String TAG = "UDOO_AndroidADKFULL";
private static String mAdkManager=null;
private ToggleButton buttonLED;
private TextView distance;
private AdkReadTask mAdkReadTask;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//
//// register a BroadcastReceiver to catch UsbManager.ACTION_USB_ACCESSORY_DETACHED action
// registerReceiver(mAdkManager.getUsbReceiver(), mAdkManager.getDetachedFilter());
buttonLED = (ToggleButton) findViewById(R.id.toggleButtonLed);
distance = (TextView) findViewById(R.id.textViewIntro);
// mAdkManager.open();
TextView tv = (TextView) findViewById(R.id.ppm);
if (mAdkManager==null){
tv.setText("ADK is null. init()");
mAdkManager = new String ("sometext");
}
else{
tv.setText("ADK is not null.");
}
if (MyApplication.sAdkManager.getInstance().checkNull()) {
distance.setText("Null before init");
MyApplication.sAdkManager.init(this);
}
if (MyApplication.sAdkManager.getInstance().checkNull()) {
distance.setText("Null after init");
}
MyApplication.sAdkManager.getInstance().open();
mAdkReadTask = new AdkReadTask();
mAdkReadTask.execute();
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onDestroy() {
MyApplication.sAdkManager.getInstance().close();
// unregisterReceiver(mAdkManager.getUsbReceiver());
super.onDestroy();
}
// ToggleButton method - send message to SAM3X
public void blinkLED(View v){
if (buttonLED.isChecked()) {
TextView tvdbg = (TextView) findViewById(R.id.ppm);
tvdbg.setText("send 1");
// writeSerial() allows you to write a single char or a String object.
//mAdkManager.writeSerial("1");
MyApplication.sAdkManager.getInstance().write("1");
// mAdkManager.writeSerial("8");
} else {
//mAdkManager.writeSerial("0");
MyApplication.sAdkManager.getInstance().write("0");
}
}
/*
* We put the readSerial() method in an AsyncTask to run the
* continuous read task out of the UI main thread
*/
private class AdkReadTask extends AsyncTask<Void, String, Void> {
private boolean running = true;
public void pause(){
running = false;
}
protected Void doInBackground(Void... params) {
// Log.i("ADK demo bi", "start adkreadtask");
while(running) {
// if (mAdkManager.serialAvailable())
// publishProgress(mAdkManager.readSerial()) ;
publishProgress(MyApplication.sAdkManager.getInstance().read());
}
return null;
}
protected void onProgressUpdate(String... progress) {
distance.setText("You put "+((int)progress[0].charAt(0)-48) + " iqos butts\tRFID OK");
next();
// Log.i(TAG, "received: " + (int)progress[0].charAt(0));
}
}
private void next() {
final Intent intent = new Intent(this, BRActivity.class );
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
mAdkReadTask.pause();
mAdkReadTask = null;
startActivity(intent);
}
},
3000);
}
}
There are just 2 Activities for now - MainActivity and BRActivity. BRActivity is just a view with "return" button which comes back to MainActivity.
Also what I find interesting - I output the readSerial in TextView to see what I got in reader thread. However on cycle#2 i don't get any output to TextView, but Activity still changes to the next one.
[EDIT]
Apparently the problem was solved when the thread was nulling. However, I still don't get the text update, but I magically get to another screen. Please advice.

Rewrite Android Function to start an Activity instead of dsplaying text

I am pretty new to Android Development and Java for that matter.
I am using an external library by a company that produces stamps that can be recognised on a touchscreen.
I get the application to start and display the stamp data, but I would like the application to start a new Activity once the stamp has been entered.
Please find the MainView.Class and StampActivity.Class below:
Mainview
import android.content.Context;
import android.content.Intent;
import android.util.AttributeSet;
import com.onetwocm.stamp.service.sdk.StampBaseView;
#SuppressWarnings("deprecation")
public class MainView extends StampBaseView {
public MainView(Context context) {
super(context);
}
public MainView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MainView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void onInitSuccess() {
StampActivity.setText("onInitSuccess");
}
public void onInitError(String code, String message) {
StampActivity.setText("onInitError - " + code + " " + message);
}
public void onStampSuccess(String stampResult) {
StampActivity.setText("onStampSuccess - " + stampResult);
}
public void onStampError(String code, String message) {
StampActivity.setText("onStampError - " + code + " " + message);
}
}
StampActivity.Class
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.widget.TextView;
public class StampActivity extends Activity {
MainView mainView = null;
static TextView textView = null;
public static final int KOREA = 0;
public static final int TAIWAN = 1;
public static final int JAPAN = 2;
public static final int KOREAN = 0;
public static final int ENGLISH = 1;
public static final int CHINESE = 2;
public static final int JAPANESE = 3;
public static final int SPANISH = 4;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_echoss);
mainView = (MainView)findViewById(R.id.mainView);
textView = (TextView)findViewById(R.id.textView);
// String APIKEY = "xxxxx";
String APIKEY = "xxxxxx"; // Write you api key
int REGION = KOREA; // KOREA TAIWAN JAPAN
int LANGUAGE = ENGLISH; // KOREAN ENGLISH CHINESE JAPANESE SPANISH
String INDEX = "B"; // Write index value received from 12cm
// Display Initialization
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
mainView.init(metric);
// Library Initialization for Stamp
mainView.PlatformInit(APIKEY, REGION, LANGUAGE, INDEX);
}
#Override
public void onBackPressed() {
super.onBackPressed();
}
public static void setText(String text) {
textView.setText(text);
}
In the MainView Class exist the function onStampSuccess. Instead of returning text, I would like it to start a new activity.
I would be grateful for any help.
You should totally avoid static methods as the current structure makes your app highly susceptible to memory leaks! This can be achieved using interfaces. Firstly, in a new class define your interface method as (may add more later):
public interface MainViewActivityInterface {
public void stampSuccess(String text)
}
Now, modify your MainView to accept the above interface:
import android.content.Context;
import android.content.Intent;
import android.util.AttributeSet;
import com.onetwocm.stamp.service.sdk.StampBaseView;
#SuppressWarnings("deprecation")
public class MainView extends StampBaseView {
private MainViewActivityInterface mMainViewActivityInterface
...
// method to accept interface var, need to call from activity
public void initMainView(MainViewActivityInterface mainViewActivityInterface) {
mMainViewActivityInterface = mainViewActivityInterface;
}
// api success callback
public void onStampSuccess(String stampResult) {
// StampActivity.setText("onStampSuccess - " + stampResult);
mMainViewActivityInterface.stampSuccess(stampResult);
}
...
}
Now modify your StampActivity as:
public class StampActivity extends Activity implements MainViewActivityInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
mainView.init(metric);
mainView.initMainView(this); // this is where we are passing the current activity as interface
...
}
#Override
public void stampSuccess(String text) {
// this is where interface callback happens, start new activity here
// may do something with text here
Intent intent = new Intent(this, NewActivity.class);
startActivity(intent);
}
}
Apply the following changes to your setText() method:
public static void setText(String text) {
textView.setText(text);
if(text.startsWith("onStampSuccess"){
Intent intent = new Intent(this, nextActivity.class);
startActivity(intent);
}

Categories

Resources