autocomplete freeze when making searchs too fast - android

I have the next code to make search of artist and songs on the spotify servers. I have an autocomplete text but my problem is if I search for something, like "David Guetta" and I try to delete with backspace ( <-- ) everytime that I delete one character it makes a search, and if I do it so fast the app crashes (heavy usage?). I don't know really if it is for that question.
What can I do to fix this? With a wait time to search it can be fixed but I don't know how to do it.
Can you help me with this? Thank you.
This is my SearchMusic.java code.
public class SearchMusic extends Activity {
AutoCompleteTextView autoCompleteSongs;
String searchTerms;
String[] arrayArtist = new String[64];
String[] arrayTrack = new String[64];
ArrayAdapter<String> adapter;
List<String> songs;
List<String> lArtist;
List<String> lTrack;
boolean bothsearchs = false; // Controlamos que haya busqueda por artista y
// pista si uno no existe.
int nArtist = 0; // iterator
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_music);
autoCompleteSongs = (AutoCompleteTextView) findViewById(R.id.autoCompletePetition);
final ArrayAdapter<String> list = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line);
// autoCompleteSongs.setThreshold(1);
// autoCompleteSongs.addTextChangedListener(this);
// autoCompleteSongs.setAdapter(new
// ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,
// item));
autoCompleteSongs.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (s.length() > 3) {
searchTerms = s.toString();
searchTerms = searchTerms.replace(" ", "+");
// Buscamos por artista
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://ws.spotify.com/search/1/artist.json?q="
+ searchTerms + "*", null,
new JsonHttpResponseHandler() {
public void onSuccess(JSONObject data) {
try {
// Hay artistas con ese nombre
if (data.length() > 0) {
JSONArray artist = new JSONArray(
data.getJSONArray("artists")
.toString());
for (int i = 0; i < 6; i++) {
JSONObject orden = artist
.getJSONObject(i);
String name = orden
.getString("name");
list.add(name);
arrayArtist[i] = name;
arrayTrack[i] = "";
nArtist++;
}
} else {
bothsearchs = true;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable arg0) {
}
});
// Buscamos por pista
client.get("http://ws.spotify.com/search/1/track.json?q="
+ searchTerms + "*", null,
new JsonHttpResponseHandler() {
public void onSuccess(JSONObject spoty) {
try {
JSONArray artist = new JSONArray(spoty
.getJSONArray("tracks")
.toString());
for (int i = nArtist; i < nArtist + 6 ; i++) {
JSONObject orden = artist
.getJSONObject(i);
String name = orden
.getString("name");
JSONArray nameArtist = new JSONArray(
orden.getJSONArray(
"artists")
.toString());
JSONObject namArt = nameArtist
.getJSONObject(0);
String nameArt = namArt
.getString("name");
list.add("[" + nameArt + "] "
+ name);
arrayArtist[i] = nameArt;
arrayTrack[i] = name;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable arg0) {
}
});
list.notifyDataSetChanged();
TextView text = (TextView) findViewById(R.id.petitionTextView);
for(int i = 0; i < 12; i++){
Log.i("AART", "" + arrayArtist[i]);
Log.i("ATRA", "" + arrayTrack[i]);
}
if(arrayArtist[0] == null && arrayTrack[0] == ""){
text.setText("No hay resultados");
}else{
for(int i = 0; i < 12; i++){
String register = "<font color=#64c7eb>" + arrayArtist[i] + "</font> <font color=#272527>" + arrayTrack[i] + "</font></br>";
text.setText(Html.fromHtml(register));
}
}
}
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.search_music, menu);
return true;
}
}
If I am right, this the code that I need. The problem is how to stop it. ¬¬
//Declare the timer
Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//Called each time when 1000 milliseconds (1 second) (the period parameter)
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
0,
//Set the amount of time between each execution (in milliseconds)
1000);

I think you are already giving a good solution.
Try wrapping the http request within a TimerTask and create a timer mechanism to cancel out the TimerTask.
Example (not tested):
public class SearchMusic extends Activity {
AutoCompleteTextView autoCompleteSongs;
String searchTerms;
String[] arrayArtist = new String[64];
String[] arrayTrack = new String[64];
ArrayAdapter<String> adapter;
List<String> songs;
List<String> lArtist;
List<String> lTrack;
//Declare and initialize the timer
Timer t = new Timer();
boolean bothsearchs = false; // Controlamos que haya busqueda por artista y
// pista si uno no existe.
int nArtist = 0; // iterator
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_music);
autoCompleteSongs = (AutoCompleteTextView) findViewById(R.id.autoCompletePetition);
final ArrayAdapter<String> list = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line);
// autoCompleteSongs.setThreshold(1);
// autoCompleteSongs.addTextChangedListener(this);
// autoCompleteSongs.setAdapter(new
// ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,
// item));
autoCompleteSongs.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (s.length() > 3) {
// Cancel the Timer and all scheduled tasks
t.cancel();
t.purge();
t = new Timer();
//Set the schedule function and rate
t.schedule(new TimerTask() {
#Override
public void run()
{
//Called each time when 1000 milliseconds (1 second) (the period parameter)
searchTerms = s.toString();
searchTerms = searchTerms.replace(" ", "+");
// Buscamos por artista
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://ws.spotify.com/search/1/artist.json?q="
+ searchTerms + "*", null,
new JsonHttpResponseHandler() {
public void onSuccess(JSONObject data) {
try {
// Hay artistas con ese nombre
if (data.length() > 0) {
JSONArray artist = new JSONArray(
data.getJSONArray("artists")
.toString());
for (int i = 0; i < 6; i++) {
JSONObject orden = artist
.getJSONObject(i);
String name = orden
.getString("name");
list.add(name);
arrayArtist[i] = name;
arrayTrack[i] = "";
nArtist++;
}
} else {
bothsearchs = true;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable arg0) {
}
});
// Buscamos por pista
client.get("http://ws.spotify.com/search/1/track.json?q="
+ searchTerms + "*", null,
new JsonHttpResponseHandler() {
public void onSuccess(JSONObject spoty) {
try {
JSONArray artist = new JSONArray(spoty
.getJSONArray("tracks")
.toString());
for (int i = nArtist; i < nArtist + 6 ; i++) {
JSONObject orden = artist
.getJSONObject(i);
String name = orden
.getString("name");
JSONArray nameArtist = new JSONArray(
orden.getJSONArray(
"artists")
.toString());
JSONObject namArt = nameArtist
.getJSONObject(0);
String nameArt = namArt
.getString("name");
list.add("[" + nameArt + "] "
+ name);
arrayArtist[i] = nameArt;
arrayTrack[i] = name;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable arg0) {
}
});
list.notifyDataSetChanged();
TextView text = (TextView) findViewById(R.id.petitionTextView);
for(int i = 0; i < 12; i++){
Log.i("AART", "" + arrayArtist[i]);
Log.i("ATRA", "" + arrayTrack[i]);
}
if(arrayArtist[0] == null && arrayTrack[0] == ""){
text.setText("No hay resultados");
}else{
for(int i = 0; i < 12; i++){
String register = "<font color=#64c7eb>" + arrayArtist[i] + "</font> <font color=#272527>" + arrayTrack[i] + "</font></br>";
text.setText(Html.fromHtml(register));
}
}
}
},
//Set how long before to start calling the TimerTask (in milliseconds)
0,
//Set the amount of time between each execution (in milliseconds)
1000);
}
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.search_music, menu);
return true;
}
}

The best thing you can do is using a handler, this is a simple example with a button. But the button represents the backspace on your code.
The idea is to schedule the search action within 500 miliseconds but when someone hits the search button again, we reschedule the search action until the user stops hitting the search button.
Good luck!
public class MyActivity extends Activity implements OnClickListener
{
protected static final int MSG_SEARCH = 0;
protected Button buttonSearch;
protected Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
switch (msg.what)
{
case MSG_SEARCH:
MyActivity.this.search();
break;
}
}
};
#Override
public void onClick(View inView)
{
if (inView == this.buttonSearch)
{
this.handler.removeMessages(MSG_SEARCH);
final Message message = Message.obtain(this.handler, MSG_SEARCH);
this.handler.sendMessageDelayed(message, 500);
}
}
protected void search()
{
// your seach code
}
}

I had the same problem when i was using GooglePlaceApi to get list of addresses. I used synchronized in the performFiltering(), publishResults() and also the method that does the rest call. It worked for me. Maybe you can give a try.
synchronized (input)
{
// Do something inside
}

Related

TextWatcher skipping characters during api call

Below code is part of search box in my android app.
LoadCatalog is a async task for the api call, problem is whenever it is being called the editText stops taking new character for a fraction of second(skips a character in middle).
for ex- if the user want to enter "The book of leaves"...
it only sometimes take "The boo of " or "The bookof "
It skips the character, pls suggest what's wrong in my code.
private TextWatcher productEntered = new TextWatcher() {
long lastChange = 0;
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence enteredSequence, int start, int before, int count) {
searchbarActionClear.setVisibility(View.VISIBLE);
enteredText = enteredSequence;
if (CommonUtils.isConnectingToInternet(DashboardActivity.this)) {
if (enteredText.length() > 3) {
new Handler().postDelayed(new Runnable() {
public void run() {
if (System.currentTimeMillis() - lastChange >= 600) {
resetList();
toolbarSuggestionEditText.setTag(toolbarSuggestionEditText.getKeyListener());
toolbarSuggestionEditText.setKeyListener(null);
new LoadCatalog().execute(String.valueOf(enteredText));
}
}
}, 600);
lastChange = System.currentTimeMillis();
}
}
}
public void afterTextChanged(Editable s) {
}
};
private class LoadCatalog extends AsyncTask<String, Void, CustomResponse> {
#Override
protected CustomResponse doInBackground(String... params) {
String url;
if (categoryItem != null) {
url = String.format(AppConstants.URLs.SEARCH_WITH_CATEGORY, params[0], categoryItem);
} else {
url = String.format(AppConstants.URLs.SEARCH, params[0]);
}
CustomResponse response = HttpRequest.GET_REQUEST(url, DashboardActivity.this);
return response;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(CustomResponse result) {
try {
if (result.getResponseCode() == 200) {
JSONArray jsonArray = null;
jsonArray = new JSONArray(result.getResponseBody());
Suggestion suggestion = null;
if (jsonArray.length() > 0) {
suggestionList.clear();
suggestionList.add(new Suggestion(null, Suggestion.TYPE_SUGGESTION_HEADER));
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
suggestion = new Suggestion(jsonObject.getString("name"),
jsonObject.getString("category"),
Suggestion.TYPE_SUGGESTION);
suggestionList.add(suggestion);
suggestionAdapter.notifyDataSetChanged();
}
toolbarSuggestionEditText.setKeyListener((KeyListener) toolbarSuggestionEditText.getTag());
} else {
toolbarSuggestionEditText.setKeyListener((KeyListener) toolbarSuggestionEditText.getTag());
Toast.makeText(DashboardActivity.this, "No item match with your search", Toast.LENGTH_SHORT).show();
suggestionList.clear();
}
} else {
toolbarSuggestionEditText.setKeyListener((KeyListener) toolbarSuggestionEditText.getTag());
}
} catch (JSONException e) {
toolbarSuggestionEditText.setKeyListener((KeyListener) toolbarSuggestionEditText.getTag());
e.printStackTrace();
}
}
}

Obtaining an array of json format question and put them into an array in android studio

The goal here is to retrieve JSON format data from an API, convert the data into an array in android studio. Then to display a random question into a text view and the question will not repeat itself. The question changes everytime a button is clicked. There's something wrong with the logic of how I use my array/parsing the data to the array. I am not sure how to proceed. Any help is appreciated
MY JSON format
{
"error": false,
"message": "Successfully retrieved",
"questions": [
{
"question": "Tell us about yourself?"
},
{
"question": "Tell us about yourself2?"
},
{
"question": "Tell us about yourself3?"
},
{
"question": "Tell us about yourself4?"
},
{
"question": "Tell us about yourself5?"
}
]
}
My code so far ( simplified to this function )
public class MainActivity extends AppCompatActivity {
// create arraylist to store question
List<String> questionList = new ArrayList<>();
// use max to decide the number of question
// use i to find out the number of questions
int i = 10;
int min = 0;
int max = i;
int[] usedInt = new int[max];
//create another array to put all the used integer inside for 0 repeition of question
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textViewQuestions = (TextView) findViewById(R.id.questionView);
usedInt = new int[i];
Random r = new Random();
int i1 = r.nextInt(max - min + 1) + min;
//generate random number, set textview the question, set int to usedint array
textViewQuestions.setText(questionList.get(i1));
usedInt[0] = i1;
//set first question
findViewById(R.id.changeQuestion).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Log.d(TAG,questionList[updateQuestions(usedInt)]);
//final int[] usedIntTemp = usedInt;
getQuestions();
int n = updateQuestions(usedInt);
textViewQuestions.setText(questionList.get(n));
//finish();
}
});
}
}
public int updateQuestions(int usedInteger[]) {
int min = 0;
int max = i;
Random r = new Random();
int i2 = r.nextInt(max - min + 1) + min;
int uInteger[] = usedInteger;
int l = 0;
while (l != max) {
if (i2 == usedInteger[l]) {
l++;
if (l == max) {
Toast.makeText(getApplicationContext(), "No other questions available", Toast.LENGTH_LONG).show();
}
} else {
usedInteger[usedInteger.length + 1] = i2;
return i2;
}
}
return i2;
}
private void getQuestions()
{
class GetQuestions extends AsyncTask<Void, Void, String> {
//private ProgressBar progressBar;
#Override
protected String doInBackground(Void... voids) {
//creating request handler object
RequestHandler requestHandler = new RequestHandler();
//creating request parameters
HashMap<String, String> params = new HashMap<>();
params.put("role_id", "1");
//returing the response
return requestHandler.sendPostRequest(URLs.URL_QUESTIONS, params);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
//displaying the progress bar while user registers on the server
//progressBar = (ProgressBar) findViewById(R.id.progressBar);
//progressBar.setVisibility(View.VISIBLE);
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
//hiding the progressbar after completion
//progressBar.setVisibility(View.GONE);
boolean Error1 = false;
try {
//converting response to json object
JSONObject obj = new JSONObject(s);
//HashMap<String, String> questionJson = new HashMap<>();
// success = obj.getBoolean("error");
if (!obj.getBoolean("error")) {
//Toast.makeText(getApplicationContext(), obj.getString("message"), Toast.LENGTH_SHORT).show();
//getting the questions from the response
JSONArray questionsJson = obj.getJSONArray("questions");
//creating a new questions object
for (i = 0; i < questionsJson.length(); i++) {
JSONObject object = questionsJson.getJSONObject(i);
questionList.add(object.getString("question"));
Log.d(TAG,"objcheck");
Log.d(TAG,object.getString("question"));
//q = c.getString("question");
//questionJson.put("question", q);
//questionList.add(questionJson);
}
finish();
//startActivity(new Intent(getApplicationContext(), MainActivity.class));
} else {
Toast.makeText(getApplicationContext(), "Some error occurred", Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
GetQuestions gq = new GetQuestions();
gq.execute();
}
Object[] array = new Object[10]; // say 10 objects
int remain = array.length;
Random rnd = new Random();
public Object next () {
if (remain == 0) {
return null;
} else {
int i = rnd.nextInt(remain--);
Object tmp = array[i];
array[i] = array[remain];
array[remain] = tmp;
return tmp;
}
}
This will generate next random question
I tried this, with
getQuestions();
String n = String.valueOf(next());
textViewQuestions.setText(n);
I also populated the array like this
for (i = 0; i < questionsJson.length(); i++) {
JSONObject object = questionsJson.getJSONObject(i);
array[i] = object.getString("question");
questionList.add(object.getString("question"));
Log.d(TAG,object.getString("question"));
}
However, the questions do repeat but will only show a set amount of questions. :( How to make the questions not repeat?

How to select an Object in my Listview

I have a RemoteCar Control app where on the MainActivity page there is a button "location" which you can click on to get redirected into another activity (locationActivity). In this activity im displaying a JSON File in a Listview and now I want to click on those objects to select them and display the location on the main page in something like a simple TextView nothing special. How can I do that?
This is my location page:
public class location extends AppCompatActivity {
private String TAG = location.class.getSimpleName();
private ListView lv;
ArrayList<HashMap<String, String>> locationList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_location);
locationList = new ArrayList<>();
lv = (ListView) findViewById(R.id.list);
new GetContacts().execute();
}
private class GetContacts extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
Toast.makeText(location.this, "Json Data is downloading", Toast.LENGTH_LONG).show();
}
#Override
protected Void doInBackground(Void... arg0) {
HttpHandler sh = new HttpHandler();
// Making a request to url and getting response
String url = "url";
String jsonStr = sh.makeServiceCall(url);
Log.e(TAG, "Response from url: " + jsonStr);
if (jsonStr != null) {
try {
//JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
//JSONArray locations = jsonObj.getJSONArray("");
JSONArray locations_ = new JSONArray(jsonStr);
// looping through All Contacts
for (int i = 0; i < locations_.length(); i++) {
JSONObject c = locations_.getJSONObject(i);
String type = c.getString("type");
String name = c.getString("name");
String address = c.getString("address");
String lat = c.getString("lat");
String lon = c.getString("lon");
String icon;
if(c.has("icon")){
//your json is having "icon" Key, get the value
icon = c.getString("icon");
}
else{
//your json is NOT having "icon" Key, assign a dummy value
icon = "/default/icon_url()";
}
// tmp hash map for single contact
HashMap<String, String> location = new HashMap<>();
// adding each child node to HashMap key => value
location.put("type", type);
location.put("name", name);
location.put("address", address );
location.put("lat", lat);
location.put("lon", lon);
location.put("icon", icon);
// adding contact to contact list
locationList.add(location);
}
} catch (final JSONException e) {
Log.e(TAG, "Json parsing error: " + e.getMessage());
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),
"Json parsing error: " + e.getMessage(),
Toast.LENGTH_LONG).show();
}
});
}
} else {
Log.e(TAG, "Couldn't get json from server.");
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),
"Couldn't get json from server. Check LogCat for possible errors!",
Toast.LENGTH_LONG).show();
}
});
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
ListAdapter adapter = new SimpleAdapter(location.this, locationList,
R.layout.list_item, new String[]{"type", "name", "address", "lat", "lon", "icon"},
new int[]{R.id.type, R.id.name, R.id.address, R.id.lat, R.id.lon, R.id.icon});
lv.setAdapter(adapter);
}
}
and this is my MainActivity page
public class MainActivity extends AppCompatActivity {
public ProgressBar fuelBar;
public Button lockButton;
public Button engButton;
public Button refuelButton;
public Button locationButton;
public SeekBar seekBarButton;
public TextView seekText;
int incFuel = 0;
final String FUELBAR = "fuelBar";
final String AC_BARTEXT = "acBarText";
final String AC_BAR = "acBar";
final String REFUELBUTTON = "refuelButton";
final String STARTENGINE = "startEngineButton";
SharedPreferences sharedPref;
SharedPreferences.Editor editor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationButton = (Button) findViewById(R.id.locationB);
lockButton = (Button) findViewById(R.id.lockB);
engButton = (Button) findViewById(R.id.engB);
refuelButton = (Button) findViewById(R.id.refuelB);
fuelBar = (ProgressBar) findViewById(R.id.fuelProgressBar);
fuelBar.setMax(100);
fuelBar.setProgress(30);
refuelButton.setText(R.string.refuelB);
lockButton.setText(R.string.lockB);
locationButton.setText(R.string.locationB);
engButton.setText(R.string.engB);
seekBarButton = (SeekBar) findViewById(R.id.seekBar);
seekText = (TextView) findViewById(R.id.seekText);
sharedPref = getPreferences(Context.MODE_PRIVATE);
editor = sharedPref.edit();
seek_bar();
lockPage();
locationPage();
}
#Override
protected void onPause(){
super.onPause();
editor.putInt(FUELBAR, fuelBar.getProgress());
editor.commit();
String tmpAC = "AC : " + String.valueOf(seekBarButton.getProgress()+18) + "°";
editor.putString(AC_BARTEXT, tmpAC);
editor.commit();
editor.putInt(AC_BAR, seekBarButton.getProgress());
editor.commit();
editor.putString(REFUELBUTTON, refuelButton.getText().toString());
editor.commit();
editor.putString(STARTENGINE, engButton.getText().toString());
editor.commit();
}
#Override
public void onResume(){
super.onResume();
fuelBar = (ProgressBar) findViewById(R.id.fuelProgressBar);
incFuel = sharedPref.getInt(FUELBAR, 0);
fuelBar.setProgress(incFuel);
seekText = (TextView) findViewById(R.id.seekText);
String tmpAC = sharedPref.getString(AC_BARTEXT, "error");
seekText.setText(tmpAC);
seekBarButton = (SeekBar) findViewById(R.id.seekBar);
int tmpInt = sharedPref.getInt(AC_BAR, 18);
seekBarButton.setProgress(tmpInt);
tmpAC = sharedPref.getString(REFUELBUTTON, "REFUEL");
refuelButton.setText(tmpAC);
tmpAC = sharedPref.getString(STARTENGINE, "START ENGINE");
engButton.setText(tmpAC);
}
#Override
public void onStop(){
super.onStop();
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.engB:
if (engButton.getText() == "ENGINE RUNNING") {
engButton.setText("START ENGINE");
} else {
if (fuelBar.getProgress() > 0) {
Toast.makeText(MainActivity.this, "starting engine..", Toast.LENGTH_SHORT).show();
engButton.setText("ENGINE RUNNING");
if (fuelBar.getProgress() >= 10) {
incFuel = fuelBar.getProgress();
incFuel -= 10;
fuelBar.setProgress(incFuel);
if (fuelBar.getProgress() < 100)
refuelButton.setText("REFUEL");
}
} else if (fuelBar.getProgress() == 0) {
Toast.makeText(MainActivity.this, "no fuel", Toast.LENGTH_SHORT).show();
engButton.setText("EMPTY GASTANK");
} else
engButton.setText("START ENGINE");
}
break;
case R.id.refuelB:
if (fuelBar.getProgress() == 0) {
engButton.setText("START ENGINE");
incFuel = fuelBar.getProgress();
incFuel += 10;
fuelBar.setProgress(incFuel);
} else if (fuelBar.getProgress() < 100) {
incFuel = fuelBar.getProgress();
incFuel += 10;
fuelBar.setProgress(incFuel);
} else {
Toast.makeText(MainActivity.this, "tank is full", Toast.LENGTH_SHORT).show();
refuelButton.setText("FULL");
}
break;
}
}
public void seek_bar() {
seekBarButton = (SeekBar) findViewById(R.id.seekBar);
seekText = (TextView) findViewById(R.id.seekText);
seekText.setText("AC : " + (seekBarButton.getProgress() + 18) + "°");
seekBarButton.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
int progressNum;
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
progressNum = progress;
seekText.setText("AC : " + (seekBarButton.getProgress() + 18) + "°");
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
seekText.setText("AC : " + (seekBarButton.getProgress() + 18) + "°");
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
seekText.setText("AC : " + (seekBarButton.getProgress() + 18) + "°");
}
});
}
public void lockPage() {
lockButton = (Button) findViewById(R.id.lockB);
lockButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent lockPage = new Intent(MainActivity.this, lockDoor.class);
startActivity(lockPage);
}
});
}
public void locationPage() {
locationButton = (Button) findViewById(R.id.locationB);
locationButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent locationPage = new Intent(MainActivity.this, location.class);
startActivity(locationPage);
}
});
}
}
Sorry for the wall of code I'm always unsure how much information to provide.
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MyActivity.this, "location:" + " "+ stringList[position] + " " + "hauptbahnhof selected", Toast.LENGTH_SHORT).show();
}
});
define your list of string as private out of onCreate

ExpandableListView with multiple choice save selected item into an array

I have an array that is filled with API data, and I have an expandablelistview to show the items of that array, now what I'm trying to do is when the user clicks on an item it saves th ID of that item into an array, so if the user select 2 items I want 2 Ids in my array, what is happening now is this: no matter if I select only one of the items it gets all of them and save it inside my array.
public class MainActivity extends Fragment {
private ExpandListAdapter ExpAdapter;
private ExpandableListView ExpandList;
private Button notificar;
private Context context;
MainActivity mContext;
private Button footer;
private double RaioEscola;
private CircleOptions mCircle;
GPSTracker tracker;
private Location location;
private Integer IdEscola;
public MainActivity() {
}
public MainActivity(Context context) {
this.context = context;
}
#Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.expand, container, false);
ExpandList = (ExpandableListView) rootView.findViewById(R.id.exp_list);
notificar = (Button) rootView.findViewById(R.id.btnGetMoreResults);
new asyncTask(MainActivity.this).execute();
return rootView;
}
private class asyncTask extends AsyncTask<String, Void, Void> {
private ProgressDialog pd;
public asyncTask(MainActivity context) {
mContext = context;
pd = new ProgressDialog(getActivity());
pd.setTitle("Por Favor Espere ...");
pd.setMessage("Enviando ...");
if (!pd.isShowing()) {
pd.show();
}
}
#Override
protected Void doInBackground(final String... params) {
try {
String[] resposta = new WebService().get("filhos");
if (resposta[0].equals("200")) {
JSONObject mJSONObject = new JSONObject(resposta[1]);
JSONArray dados = mJSONObject.getJSONArray("data");
/* cria o array que vai receber os dados da api */
final ArrayList<Escolas> mArrayList = new ArrayList<Escolas>();
/* percorre o array, adicionando cada linha encontrada em um ArrayList */
for (int i = 0; i < dados.length(); i++) {
JSONObject item = dados.getJSONObject(i);
Escolas mEscolas = new Escolas();
mEscolas.setId_escola(item.optInt("id_escola"));
mEscolas.setCnpj(item.getString("cnpj"));
mEscolas.setRazao_social(item.getString("razao_social"));
mEscolas.setNome_fantasia(item.getString("nome_fantasia"));
mEscolas.setDistancia(Float.valueOf(item.getString("distancia")));
mEscolas.setLogradouro(item.optString("logradouro"));
mEscolas.setNumero(item.optString("numero"));
mEscolas.setBairro(item.getString("bairro"));
mEscolas.setComplemento(item.getString("complemento"));
mEscolas.setCep(item.getString("cep"));
mEscolas.setCidade(item.getString("cidade"));
mEscolas.setEstado(item.getString("estado"));
mEscolas.setLatitude(Float.parseFloat(item.getString("latitude")));
mEscolas.setLongitude(Float.parseFloat(item.getString("longitude")));
RaioEscola = Double.parseDouble(String.valueOf(mEscolas.getDistancia()));
IdEscola = mEscolas.getId_escola();
JSONObject alunos = item.optJSONObject("alunos");
JSONArray data = alunos.getJSONArray("data");
if (data != null) {
ArrayList<Filhos> arrayalunos = new ArrayList<Filhos>();
for (int a = 0; a < data.length(); a++) {
Filhos mFilhos = new Filhos();
JSONObject clientes = data.getJSONObject(a);
mFilhos.setId_aluno(clientes.optInt("id_aluno"));
mFilhos.setNome(clientes.optString("nome"));
mFilhos.setSobrenome(clientes.optString("sobrenome"));
mFilhos.setFoto(clientes.optString("foto"));
mFilhos.setModalidade_de_ensino(clientes.optString("modalidade_de_ensino"));
mFilhos.setObservacoes(clientes.optString("observacoes"));
arrayalunos.add(mFilhos);
}
mEscolas.setalunos(arrayalunos);
}
/* popula o array de viagens */
mArrayList.add(mEscolas);
ExpAdapter = new ExpandListAdapter(getActivity(), mArrayList);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
ExpandList.setAdapter(ExpAdapter);
ExpAdapter.notifyDataSetChanged();
ExpAdapter.setChoiceMode(ExpandListAdapter.CHOICE_MODE_MULTIPLE);
ExpandList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(final ExpandableListView parent, View v, final int groupPosition, final int childPosition, final long id) {
ExpAdapter.setClicked(groupPosition, childPosition);
final int index = parent.getFlatListPosition(ExpandableListView.getPackedPositionForChild(groupPosition, childPosition));
parent.setItemChecked(index, true);
parent.setSelectedChild(groupPosition, childPosition, true);
parent.getChildAt(index);
notificar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
class update extends TimerTask {
public void run() {
try {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
final float latitude = mArrayList.get(groupPosition).getLatitude();
final float longitude = mArrayList.get(groupPosition).getLongitude();
LatLng latLng = new LatLng(latitude, longitude);
drawMarkerWithCircle(latLng);
GPSTracker gps = new GPSTracker(getActivity());
double latitudegps = gps.getLatitude();
double longitudegps = gps.getLongitude();
float[] distance = new float[2];
Location.distanceBetween(mCircle.getCenter().latitude, mCircle.getCenter().longitude, latitudegps, longitudegps, distance);
if (distance[0] > mCircle.getRadius()) {
Toast.makeText(getActivity(), "Outside", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getActivity(), "Inside", Toast.LENGTH_LONG).show();
AlertRest mAlertRest = new AlertRest();
try {
List<Integer> myIdList = new ArrayList<Integer>();
for (int i = 0; i < mArrayList.get(groupPosition).getalunos().size(); i++) {
Integer Idalunos = mArrayList.get(groupPosition).getalunos().get(i).getId_aluno();
myIdList.add(Idalunos);
}
mAlertRest.getNotificacao(1, mArrayList.get(groupPosition).getId_escola(), String.valueOf(myIdList), latitudegps, longitudegps);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Timer timer = new Timer();
timer.schedule(new update(), 0, 15000);
}
private void drawMarkerWithCircle(LatLng position) {
mCircle = new CircleOptions().center(position).radius(RaioEscola);
}
});
return false;
}
});
}
});
}
/* retorna um array de objetos */
}
else {
throw new Exception("[" + resposta[0] + "] ERRO: " + resposta[1]);
}
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void cursor) {
if (pd.isShowing()) {
pd.dismiss();
}
}
}
}
AlertRest:
public class AlertRest extends WebService {
private String recurso = "notificacoes";
private String[] resposta;
private AlertModelo mAlertModelo;
public AlertModelo getNotificacao(Integer id_usuario, String token, Integer id_escola, String ids_aluno, Double latitude, Double longitude) throws Exception {
/* dispara a requisição para a API retornar os dados do "recurso" necessário */
resposta = new WebService().postToken(recurso, token, "{\"id_usuario\":" + id_usuario + "," + "\"id_escola\":" + id_escola + "," + "\"ids_aluno\":\"" + ids_aluno + "\"," + "\"latitude\":" + latitude + "," + "\"longitude\":" + longitude + "}");
JSONObject mJSONObject = new JSONObject(resposta[1]);
if (resposta[1].equals("201")) {
mAlertModelo = new AlertModelo();
mAlertModelo.setId_usuario(mJSONObject.getInt(String.valueOf(id_usuario)));
mAlertModelo.setId_escola(mJSONObject.getInt(String.valueOf(id_escola)));
mAlertModelo.setIds_aluno(mJSONObject.getInt(String.valueOf(ids_aluno)));
mAlertModelo.setLatitude(mJSONObject.getDouble(String.valueOf(latitude)));
mAlertModelo.setLongitude(mJSONObject.getDouble(String.valueOf(longitude)));
}
return mAlertModelo;
}
}
The code is difficult to follow due to few nested loops and running tasks.
Anyway, I suspect the code below is causing your app to save all (unselected) items in the list:
for (int i = 0; i < mArrayList.get(groupPosition).getalunos().size(); i++) {
Integer Idalunos = mArrayList.get(groupPosition).getalunos().get(i).getId_aluno();
myIdList.add(Idalunos);
}
Note: Iterating through the entire list is a suspect. It should pick a certain item or items in the list.
So...another set of relevant codes can help us determine the code fix. I am not certain if there is a bug here also.
parent.setItemChecked(index, true);
parent.setSelectedChild(groupPosition, childPosition, true);
parent.getChildAt(index);
Note: These codes may be fine. The key code, I think, is the index. I am not too familiar with ExpandableListView.
Finally, suggested code fix:
Integer Idalunos = mArrayList.get(groupPosition).getalunos().get(index).getId_aluno();
...
myIdList.add(Idalunos);
Notice the variable index is used to get the correct item, Idalunos.
You are using mArrayList variable for initializing adapter and for filling myIdList at the same time. Of course myIdList would contain full list of your adapter.
UPDATE
It would be better to define myIdList as field of your MainActivity class and initialize it in onCreateView method.
On click not just add the value - you should check if this value is allready in list:
if(myIdList.contains(Idalunos)) {
myIdList.remove(Idalunos);
} else {
myIdList.add(Idalunos);
}

AsynTask with Endless Listview Scroll in android

I am creating an application where in i need to have endless scrolling listview. I dnt want to use any library in my application. I have seen some examples on line that help in achieving such listview,but my doubt is how can i have an endless listview when my data are coming from server and are getting parsed in the Asynctask. How can i load 10 items at a time from my asynctask on scroll? I want to know to implement a endless listview on asyntask.
Do i call my asynctask in the onScroll() or not???
public class EndlessScrollExample extends ListActivity {
public JSONArray jsonarray,jsondatearray;
public String url;
public String selectedvalue;
public String TAG = "TAG Event Display";
public String SuggestCity;
public String SuggestState;
public String suggestCountry;
public String event_id,address;
String lat;
String lng;
public String event_name;
public String dateKey;
public String datetime,timenew;
Calendar cal;
public SharedPreferences prefs;
public Editor editor;
public String access_token,id,username;
public static ArrayList<EventsBean> arrayEventsBeans = new ArrayList<EventsBean>();
ArrayList<DateBean> sortArray = new ArrayList<DateBean>();
public SAmpleAdapter adapter;
public ImageView img_menu,img_calender;
public ListView listview;
public EventsBean eventsbean;
int counter = 0;
int currentPage = 0;
FetchEventValues fetchValues;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme);
setContentView(R.layout.sample_endless);
listview = (ListView)findViewById(android.R.id.list);
try {
// Preferences values fetched from the preference of FBConnection class.
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
access_token = prefs.getString("access_token", null);
id = prefs.getString("uid", null);
username = prefs.getString("username", null);
if(access_token == null && id == null && username == null)
{
Toast.makeText(getApplicationContext(), "FaceBook Login was not successful" +
"/nPlease Relogin.", Toast.LENGTH_SHORT).show();
}
else
{
Log.i(TAG, "VALUES::" + access_token+ " " + id + " " +username);
url = "my Url";
}
} catch (NullPointerException e) {
Log.i(TAG, "User Not Logged IN " + e.getMessage());
// TODO Auto-generated catch block
e.printStackTrace();
}
fetchValues = new FetchEventValues();
fetchValues.execute();
listview = getListView();
listview.setOnScrollListener(new EndlessScrollListener());
}
// AsyncTask Class called in the OnCreate() when the activity is first started.
public class FetchEventValues extends AsyncTask<Integer, Integer, Integer>
{
ProgressDialog progressdialog = new ProgressDialog(EndlessScrollExample.this);
#SuppressLint("SimpleDateFormat")
#SuppressWarnings("unchecked")
#Override
protected Integer doInBackground(Integer... params) {
currentPage++;
// Creating JSON Parser instance
JsonParser jParser = new JsonParser();
// getting JSON string from URL
//arrayEventsBeans.clear();
JSONObject jsonobj = jParser.getJSONFromUrl(url);
Log.i(TAG, "URL VALUES:" + url);
try{
// Code to get the auto complete values Autocomplete Values
JSONArray jsonAarray = jsonobj.getJSONArray(Constants.LOCATIONS);
eventsbean = new EventsBean();
Log.e(TAG, "Location Array Size:" + jsonAarray.length());
for(int j = 0 ; j < jsonAarray.length() ; j++)
{
if(!jsonAarray.getJSONObject(j).isNull(Constants.LOCATION_CITY) && !jsonAarray.getJSONObject(j).isNull(Constants.LOCATION_STATE) && !jsonAarray.getJSONObject(j).isNull(Constants.LOCATION_COUNTRY))
{
JSONObject job = jsonAarray.getJSONObject(j);
if(job.has(Constants.LOCATION_STATE))
{
SuggestCity = job.getString(Constants.LOCATION_CITY);
eventsbean.setLocation_city(job.getString(Constants.LOCATION_CITY));
SuggestState = job.getString(Constants.LOCATION_STATE);
eventsbean.setLocation_state(job.getString(Constants.LOCATION_STATE));
suggestCountry = job.getString(Constants.LOCATION_COUNTRY);
eventsbean.setLocation_country(job.getString(Constants.LOCATION_COUNTRY));
}
}
}
// JSON object to fetch the events in datewise format
JSONObject eventobject = jsonobj.getJSONObject("events");
arrayEventsBeans = new ArrayList<EventsBean>();
// #SuppressWarnings("unchecked")
Iterator<Object> keys = eventobject.keys();
while (keys.hasNext()) {
String datestring = String.valueOf(keys.next());
if (datestring.trim().length() > 0) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date date = formatter.parse(datestring);
DateBean dateBean = new DateBean(date);
sortArray.add(dateBean);
}
// JSONArray jsonArray = eventobject.getJSONArray(datestring);
//System.out.println(" --"+jsonArray);
}
System.out.println("size:"+sortArray.size());
System.out.println("==========sorting array======");
Collections.sort(sortArray,new CompareDate());
//reverse order
//Collections.reverse(sortArray);
for(DateBean d : sortArray){
dateKey = new SimpleDateFormat("yyyy-MM-dd").format(d.getDate());
System.out.println(dateKey);
Date today = new Date();
Date alldates = d.getDate();
/// Calendar alldates1 = Calendar.getInstance();
JSONArray jsonArray = eventobject.getJSONArray(dateKey);
System.out.println(" --"+jsonArray);
for (int i = 0 ; i < jsonArray.length() ; i++)
{
if ((today.compareTo(alldates) < 0 || (today.compareTo(alldates)== 0)))
// if (alldates1 > cal) alldates.getTime() >= today.getTime()
{
String currentTimeStr = "7:04 PM";
Date userDate = new Date();
String userDateWithoutTime = new SimpleDateFormat("yyyyMMdd").format(userDate);
String currentDateStr = userDateWithoutTime + " " + currentTimeStr;
Date currentDate = new SimpleDateFormat("yyyyMMdd h:mm a").parse(currentDateStr);
if (userDate.compareTo(currentDate) >= 0) {
System.out.println(userDate + " is greater than or equal to " + currentDate);
} else {
System.out.println(userDate + " is less than " + currentDate);
}
JSONObject jsonobjname = jsonArray.getJSONObject(i);
EventsBean eventsbean = new EventsBean();
JSONObject jobjectpicture = jsonobjname.getJSONObject(Constants.PICTURE);
JSONObject jobjeventpicture = jobjectpicture.getJSONObject(Constants.DATA);
eventsbean.setUrl(jobjeventpicture.getString(Constants.URL));
if(jsonobjname.has(Constants.OWNER))
{
JSONObject owner_obj = jsonobjname.getJSONObject(Constants.OWNER);
eventsbean.setOwner_id(owner_obj.getString(Constants.OWNER_ID));
eventsbean.setOwner_name(owner_obj.getString(Constants.OWNER_NAME));
String owner_name = owner_obj.getString(Constants.OWNER_NAME);
Log.i(TAG, "Owner:" + owner_name);
}
if(!jsonobjname.isNull(Constants.COVER))
{
JSONObject objectcover = jsonobjname.getJSONObject(Constants.COVER);
eventsbean.setCover_id(objectcover.getString(Constants.COVER_ID));
eventsbean.setSource(objectcover.getString(Constants.SOURCE));
String cover_url = objectcover.getString(Constants.SOURCE);
Log.i(TAG, "Cover Url:" + cover_url);
eventsbean.setOffset_y(objectcover.getString(Constants.OFFSET_Y));
eventsbean.setOffset_x(objectcover.getString(Constants.OFFSET_X));
}
eventsbean.setName(jsonobjname.getString(Constants.NAME));
eventsbean.setEvent_id(jsonobjname.getString(Constants.EVENT_ID));
eventsbean.setStart_time(jsonobjname.getString(Constants.START_TIME));
eventsbean.setDescription(jsonobjname.getString(Constants.DESCRIPTION));
eventsbean.setLocation(jsonobjname.getString(Constants.LOCATION));
if(!jsonobjname.isNull(Constants.IS_SILHOUETTE))
{
eventsbean.setIs_silhouette(jsonobjname.getString(Constants.IS_SILHOUETTE));
}
eventsbean.setPrivacy(jsonobjname.getString(Constants.PRIVACY));
datetime = jsonobjname.getString(Constants.START_TIME);
if(!jsonobjname.isNull(Constants.VENUE))
{
JSONObject objectvenue = jsonobjname.getJSONObject(Constants.VENUE);
if(objectvenue.has(Constants.VENUE_NAME))
{
eventsbean.setVenue_name(objectvenue.getString(Constants.VENUE_NAME));
event_name = objectvenue.getString(Constants.VENUE_NAME);
Log.i(TAG, "Event Venue Name:" + event_name);
}
else
{
eventsbean.setLatitude(objectvenue.getString(Constants.LATITUDE));
eventsbean.setLongitude(objectvenue.getString(Constants.LONGITUDE));
eventsbean.setCity(objectvenue.getString(Constants.CITY));
eventsbean.setState(objectvenue.getString(Constants.STATE));
eventsbean.setCountry(objectvenue.getString(Constants.COUNTRY));
eventsbean.setVenue_id(objectvenue.getString(Constants.VENUE_ID));
eventsbean.setStreet(objectvenue.getString(Constants.STREET));
address = objectvenue.getString(Constants.STREET);
eventsbean.setZip(objectvenue.getString(Constants.ZIP));
}
}
arrayEventsBeans.add(eventsbean);
Log.i(TAG, "arry list values:" + arrayEventsBeans.size());
}
}
}
}catch(Exception e){
Log.e(TAG , "Exception Occured:" + e.getMessage());
}
return null;
}
class CompareDate implements Comparator<DateBean>{
#Override
public int compare(DateBean d1, DateBean d2) {
return d1.getDate().compareTo(d2.getDate());
}
}
#Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(Integer result)
{
// TODO Auto-generated method stub
super.onPostExecute(result);
if(this.progressdialog.isShowing())
{
this.progressdialog.dismiss();
}
if(adapter == null)
{
adapter = new SAmpleAdapter(EndlessScrollExample.this, 0, arrayEventsBeans);
listview.setAdapter(adapter);
}
else
{
adapter.notifyDataSetChanged();
}
//currentPage++;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
this.progressdialog.setMessage("Loading....");
this.progressdialog.setCanceledOnTouchOutside(false);
this.progressdialog.show();
}
public int setPage(int currentPage) {
return currentPage;
// TODO Auto-generated method stub
}
}
public class EndlessScrollListener implements OnScrollListener {
private int visibleThreshold = 0;
private int currentPage = 0;
public EndlessScrollListener() {
}
public EndlessScrollListener(int visibleThreshold) {
this.visibleThreshold = visibleThreshold;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
if (listview.getLastVisiblePosition() >= listview.getCount() - visibleThreshold) {
currentPage++;
fetchValues.setPage(currentPage);
fetchValues.execute();
}
}
}
}
}
Thanks in advance.
ListView already supports OnScrollListener, so you have to override it and check the condition(in onScroll()), whether it is reached to the end of the list or not.If yes, then add a footer(optional) and fire the async task. After reciving the result notify the adapter. You could check solution on this link, it works on the same concept.
here are few examples of what you are looking for:
http://mobile.dzone.com/news/android-tutorial-dynamicaly
https://github.com/johannilsson/android-pulltorefresh

Categories

Resources