Custom Adapter Listview Android - android

I hit my head against the wall, trying to solve this problem for some time. I'm not an experienced Android guy, so its solution might be really simple. That being said, I have the following code:
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.adminusers);
super.onCreate(savedInstanceState);
// set user_type for Viewall
mUserType = 0;
lv_adminusersListContent.addFooterView(footerView);
mUsersAdapter = new AdminUsersAdapter(mUsersList, AdminUsers.this);
lv_adminusersListContent.setAdapter(mUsersAdapter);
getUsersList();
}
private void getUsersList() {
if (UsefulFcts.isOnline(AdminUsers.this)) {
if (mThread != null && mThread.isAlive()) {
mThread.interrupt();
}
mThread = new Thread() {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
pDialog = new ProgressDialog(AdminUsers.this);
pDialog.setMessage("Loading.. Please wait!");
pDialog.setCancelable(false);
pDialog.show();
}
});
mUsersList.clear();
mUsersList.addAll(mServiceManager.getUsersList(mUserType,
0 + "", AdminServiceManager.sHowManyToLoad));
mArrayLength = mUsersList.size();
Log.d("arrayLength", String.valueOf(mArrayLength));
Log.d("total_results",
String.valueOf(ParserJSON.total_results));
mHandler.post(new Runnable() {
#Override
public void run() {
// refresh listview
mUsersAdapter.notifyDataSetChanged();
if (mArrayLength < ParserJSON.total_results) {
lv_adminusersListContent
.addFooterView(footerView);
} else if (mArrayLength >= ParserJSON.total_results) {
lv_adminusersListContent
.removeFooterView(footerView);
}
if (0 == mArrayLength) {
Toast.makeText(AdminUsers.this,
"No username found!",
Toast.LENGTH_SHORT).show();
}
pDialog.dismiss();
}
});
}
};
mThread.start();
} else {
Toast.makeText(AdminUsers.this, "Internet connection required!",
Toast.LENGTH_LONG).show();
}
}
#Override
protected void linkUI() {
// begin load_more view
footerView = AdminUsers.this.getLayoutInflater().inflate(
R.layout.loadmore, null);
btn_loadmore = (Button) footerView.findViewById(R.id.btn_loadmore);
// end load_more view
btn_adminusersViewall = (Button) findViewById(R.id.btn_adminusersViewall);
btn_adminusersAdmins = (Button) findViewById(R.id.btn_adminusersAdmins);
btn_adminusersClients = (Button) findViewById(R.id.btn_adminusersClients);
btn_adminusersDevs = (Button) findViewById(R.id.btn_adminusersDevs);
btn_adminAdd = (ImageButton) findViewById(R.id.btn_adminAdd);
lv_adminusersListContent = (ListView) findViewById(R.id.lv_adminusersListContent);
}
#Override
protected void setAction() {
// begin Buttons UnderHeader
btn_adminusersViewall.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UsefulFcts.setButtonClicked(btn_adminusersViewall,
btn_adminusersClients, btn_adminusersAdmins,
btn_adminusersDevs);
mUserType = 0;
mArrayLength = -1;
lv_adminusersListContent.removeFooterView(footerView);
getUsersList();
}
});
btn_adminusersClients.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UsefulFcts.setButtonClicked(btn_adminusersClients,
btn_adminusersDevs, btn_adminusersAdmins,
btn_adminusersViewall);
mUserType = 1;
mArrayLength = -1;
// view.setVisibility(View.GONE);
getUsersList();
}
});
btn_adminusersDevs.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UsefulFcts.setButtonClicked(btn_adminusersDevs,
btn_adminusersClients, btn_adminusersAdmins,
btn_adminusersViewall);
mUserType = 2;
mArrayLength = -1;
lv_adminusersListContent.removeFooterView(footerView);
getUsersList();
}
});
btn_adminusersAdmins.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mUserType = 3;
mArrayLength = -1;
lv_adminusersListContent.removeFooterView(footerView);
getUsersList();
}
});
// end Buttons UnderHeader
btn_loadmore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mThread2 = new Thread() {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
progressBar = new ProgressDialog(
AdminUsers.this);
progressBar
.setMessage("Loading more projects.. ");
progressBar.setCancelable(true);
progressBar.show();
}
});
mUsersList.addAll(mServiceManager.getUsersList(
mUserType, String.valueOf(mArrayLength),
AdminServiceManager.sHowManyToLoad));
mArrayLength = mUsersList.size();
mHandler.post(new Runnable() {
#Override
public void run() {
mUsersAdapter.notifyDataSetChanged();
if (mArrayLength >= ParserJSON.total_results) {
lv_adminusersListContent.removeFooterView(footerView);
}
progressBar.dismiss();
}
});
};
};
mThread2.start();
}
});
Note: I basically have a listview which content is requested from a server depending on which button (looking like a tab) I'm pressing (mUserType = 0/1/2/3). I also use a Load More button (footerView = simple layout with a button).
Everything seems to work just fine, but after "playing" with the buttons a few times (random number!) it crashes, with the following exception thrown:
02-21 11:00:49.252: E/AndroidRuntime(11728): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296461, class android.widget.ListView) with Adapter(class android.widget.HeaderViewListAdapter)]
So, I looked back at the code, but couldn't see any "not on UI thread" listview notification.
1. What could the problem be?
2. Is it necessary to use 2 threads (mThread and mThread2), or could I use the same one, as one's work should start when the other ends (or isn't it ?) and should I declare them static or not ?
Also pointing me to any "Good practices" tricks I could use in the future, or "Wrong practices" that I did would be much appreciated!
PS. linkUI() and setAction() are called in super().onCreate().

The error message seems understandable : you are modifying the adapter from mThread:
mUsersList.clear();
mUsersList.addAll(mServiceManager.getUsersList(mUserType,
0 + "", AdminServiceManager.sHowManyToLoad));
You can try something like this:
final List newUserList = mServiceManager.getUsersList(mUserType,
0 + "", AdminServiceManager.sHowManyToLoad);
mArrayLength = newUserList.size();
Log.d("arrayLength", String.valueOf(mArrayLength));
Log.d("total_results",
String.valueOf(ParserJSON.total_results));
mHandler.post(new Runnable() {
#Override
public void run() {
mUsersList.clear();
mUsersList.addAll(newUserList);
// refresh listview
mUsersAdapter.notifyDataSetChanged();

Related

How to get to next object in foreach

Im making a quizz app in android studio.I have a slight issue. I display the questions and answers fine, but when the user click on a button, be it right or wrong answer, i want the next question to come.
I feel its really stupid, but heres what i did.
private void getListe() {
new Thread(new Runnable() {
#Override
public void run() {
QuizzController quizzController = new QuizzController(GameActivity.this);//bon contexte?
liste = quizzController.getGame(difficulte, categorie, nb);
handlerGame.post(new Runnable() {
#Override
public void run() {
Toast.makeText(GameActivity.this,"gg", Toast.LENGTH_SHORT).show();
for(QuizzRoom q : liste ){
tvQuestion.setText(q.question);
btnBadAnswer.setText(q.mauvaisesReponses);
btnGoodAnswer.setText(q.bonneReponse);
}
btnGoodAnswer.setOnClickListener(v -> {
score+=10;
tvScore.setText(String.valueOf(score));
});
}
});
}
}).start();private void getListe() {
new Thread(new Runnable() {
#Override
public void run() {
QuizzController quizzController = new QuizzController(GameActivity.this);//bon contexte?
liste = quizzController.getGame(difficulte, categorie, nb);
handlerGame.post(new Runnable() {
#Override
public void run() {
Toast.makeText(GameActivity.this,"gg", Toast.LENGTH_SHORT).show();
for(QuizzRoom q : liste ){
tvQuestion.setText(q.question);
btnBadAnswer.setText(q.mauvaisesReponses);
btnGoodAnswer.setText(q.bonneReponse);
}
btnGoodAnswer.setOnClickListener(v -> {
score+=10;
tvScore.setText(String.valueOf(score));
});
}
});
}
}).start();
I cannot figure how to get to the next QuizzRoom object (answer+questions). Feels like it should just come right on when the loop starts over, but it doesnt.
Thanks in advance for any tips in the right direction.
This is not the best way but it should work:
private int questionsCount;
new Thread(new Runnable() {
#Override
public void run() {
QuizzController quizzController = new QuizzController(GameActivity.this);//bon contexte?
liste = quizzController.getGame(difficulte, categorie, nb);
//get the size of the list
questionsCount = liste.size()
handlerGame.post(new Runnable() {
#Override
public void run() {
Toast.makeText(GameActivity.this,"gg", Toast.LENGTH_SHORT).show();
btnGoodAnswer.setOnClickListener(v -> {
score+=10;
//will remove the last question form the count
questionsCount--
tvScore.setText(String.valueOf(score));
showQuestion(liste)
});
}
});
}
}).start();
void showQuestion(List<QuizzRoom> questions){
//here you should check if there are any questions left
// if(questionsCount < 0) do something
QuizzRoom q = questions[questionsCount]
tvQuestion.setText(q.question);
btnBadAnswer.setText(q.mauvaisesReponses);
btnGoodAnswer.setText(q.bonneReponse);
}

Seekbar is still not updating

I made these two method to update seekbar every 100ms:
public void updateSeekBar() {
handler.postDelayed(mUpdateTimeTask, 100);
}
private Runnable mUpdateTimeTask = new Runnable() {
#Override
public void run() {
mySeekBar.setMax(mySong.getDuration());
x = mySong.getCurrentPosition();
mySeekBar.setProgress(x);
handler.postDelayed(this, 100);
}
};
and put it inside my playMusic method:
public void playMusic() {
//just a test from intent.getExtra
if(test.equalsIgnoreCase("Jason Mraz")) {
mySong = MediaPlayer.create(MusicClass.this, jm[musicCounter]);
displaySong(jm);
songNumbers = jm.length;
}else if(test.equalsIgnoreCase("fob")) {
mySong = MediaPlayer.create(MusicClass.this, fob[musicCounter]);
displaySong(fob);
songNumbers = fob.length;
}else if(test.equalsIgnoreCase("ed")) {
mySong = MediaPlayer.create(MusicClass.this, ed[musicCounter]);
displaySong(ed);
songNumbers = ed.length;
}
//when the song is completed
mySong.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
nextSong();
}
});
//seekbar update
mySeekBar.setMax(mySong.getDuration());
mySeekBar.setProgress(0);
mySong.start();
updateSeekBar();
}
this is my onCreate method:
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.music);
artistName = (TextView)findViewById(R.id.artistName);
song = (TextView)findViewById(R.id.song);
musicCounter = 0;
ifPlaying = true;
isRandom = false;
random = (ImageButton) findViewById(R.id.random);
stop = (ImageButton)findViewById(R.id.stop);
myImageView = (ImageView)findViewById(R.id.myImageView);
dice = new Random();
mySeekBar = (SeekBar)findViewById(R.id.mySeekBar);
test = getIntent().getStringExtra("test");
if(test.equalsIgnoreCase("Jason Mraz")) {
artistName.setText("Jason Mraz");
displayPP();
songNumbers = jm.length;
myImageView.setImageResource(R.drawable.jason_mraz);
} else if (test.equalsIgnoreCase("fob")) {
artistName.setText("Fall Out Boys");
displayPP();
songNumbers = fob.length;
myImageView.setImageResource(R.drawable.fall_out_boys);
} else if (test.equalsIgnoreCase("ed")) {
artistName.setText("Ed Sheeran");
displayPP();
songNumbers = ed.length;
myImageView.setImageResource(R.drawable.ed_sheeran);
}
playMusic();
mySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (b) {
mySong.seekTo(i);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
mySong.pause();
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (ifPlaying == true) {
mySong.start();
} else {
return;
}
}
});
}
My application says Unfortunately stopped. But when I remove the updateSeekbar in playMusic method it works fine, but without the updating seekBar every second. The setOnSeekBarChangeListener works perfectly fine, the only problem is I can't make updateSeekBar method work because it is alwats stopping my application and force exit.
The symptoms you are describing is called an ANR. It means that you is doing to much work in the main thread.
What you need todo is review what you are triggering in your mUpdateTimeTask.
Next make sure your handler is running on the UI since your are updating views:
Handler handler = new Handler(Looper.getMainLooper());

I want to stop my thread when press the back button and on button click (Android studio)

my android application has two activities, when i press the on click button in main activity its move to 2nd activity and read the Bluetooth serial data values using following thread function. I want to stop the thread when i press the back button. or when press the on-click button on 2nd activity. Please help I'm new to android.
my thread function
void beginListenForData()
{
final Handler handler = new Handler();
stopThread = false;
buffer = new byte[1024];
Thread thread = new Thread(new Runnable()
{
public void run()
{
while(!Thread.currentThread().isInterrupted() && !stopThread)
{
try
{
int byteCount = inputStream1.available();
if(byteCount > 0)
{
byte[] rawBytes = new byte[byteCount];
inputStream1.read(rawBytes);
final String string=new String(rawBytes,"UTF-8");
handler.post(new Runnable() {
public void run()
{
recDataString.append(string); //keep appending to string until ~
//keep appending to string until ~
int endOfLineIndex = recDataString.indexOf("~"); // determine the end-of-line
if (endOfLineIndex > 0) { // make sure there data before ~
// String dataInPrint = string.substring(0, endOfLineIndex); // extract string
if (recDataString.charAt(0) == '#') //if it starts with # we know it is what we are looking for
{
String sensor0 = recDataString.substring(1, 4); //get sensor value from string between indices 1-5
String sensor1 = recDataString.substring(5,9); //same again...
stringToFloat1 = Float.parseFloat(sensor0);
stringToFloat2 = Float.parseFloat(sensor1);
floatToInt1 = (int)stringToFloat1 ;
floatToInt2 = (int)stringToFloat2;
if ((floatToInt1 != lastFloatToInt1) ||(floatToInt2 != lastFloatToInt2 )){
Log.d("hell=" , "ohhhhhhhhhhhhhhhh");
movieMethod();
}
lastFloatToInt1 = floatToInt1 ;
lastFloatToInt2 = floatToInt2 ;
}
recDataString.delete(0, recDataString.length()); //clear all string data
}
}
});
}
}
catch (IOException ex)
{
stopThread = true;
}
}
}
});
thread.start();
}
As I understand, you know how to stop thread but you don't know the where you put the stopThread's flag? then below is the points.
public class SecondActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_activity);
Button myButton = findViewById(R.id.my_button_id_from_xml);
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
stopThread = true;
}
});
}
//Back button listener
#Override
public void onBackPressed() {
stopThread = true;
super.onBackPressed();
}
}
Use this:
Button button;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_activity);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
yourThread.interrupt();
}
});
}
//Back button listener
#Override
public void onBackPressed() {
yourThread.interrupt();
super.onBackPressed();
}
}
You can use Activity overriden Method-:
#Override
public void onBackPressed() {
super.onBackPressed();
//Do your stuff here
}

Android remove all recyclerview with animation on a button click

I have a recycler view. On a button click I want to remove all the items from the recyclerview but the items must be removed with animation.
I am able to remove all the items at once but I don't know how to remove them with animation. Thanks
It's old, but wish this helps someone else as it's already not answered yet; I have done it by deleting a single item at a time by simulating a swipe animation on this item, and post a delay before deleting the next item, and so on to the way down to the last item of the RecyclerView
Step No.1:
In your activity that holds the clear all button and the RecyclerView instance: Create a method of single item delete
private void deleteItem(View rowView, final int position) {
Animation anim = AnimationUtils.loadAnimation(requireContext(),
android.R.anim.slide_out_right);
anim.setDuration(300);
rowView.startAnimation(anim);
new Handler().postDelayed(new Runnable() {
public void run() {
if (myDataSource.size() == 0) {
addEmptyView(); // adding empty view instead of the RecyclerView
return;
}
myDataSource.remove(position); //Remove the current content from the array
myRVAdapter.notifyDataSetChanged(); //Refresh list
}
}, anim.getDuration());
}
Step No.2:
Create the method that will delete all RecyclerView list items >> call it in your button click callback.
boolean mStopHandler = false;
private void deleteAllItems() {
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if (myDataSource.size() == 0) {
mStopHandler = true;
}
if (!mStopHandler) {
View v = myRecyclerView.findViewHolderForAdapterPosition(0).itemView;
deleteItem(v, 0);
} else {
handler.removeCallbacksAndMessages(null);
}
handler.postDelayed(this, 250);
}
};
requireActivity().runOnUiThread(runnable);
}
Also it's important to handle configuration change in manifest, activity section, as if the configuration changes while clearing your recycler view list, an exception will be raised
<activity
android:name=".activities.MainActivity"
android:configChanges="orientation|screenSize|keyboard"
android:label="#string/app_name">
...
</activity>
This is a pretty good library and what's better is the documentation for it. You can even insert durations for transitions and animations.
Also, remember that if you are using default animation, after calling myDataSet.remove(pos) using adapter.notifyDataSetChanged() while there is an animation ongoing will cause the animation to stop.
Extend BaseItemAnimator class of recyclerview-animators library:
MyAdapter adapter = new MyAdapter(null);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new MyScaleInLeftAnimator());
findViewById(R.id.button).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
int count = adapter.getItemCount();
adapter.clear();
adapter.notifyItemRangeRemoved(0, count);
}
}
);
...
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder{
private ArrayList<String> mItems;
...
public void clear() {
if (mItems != null) {
mItems.clear();
}
}
}
...
public class MyScaleInLeftAnimator extends BaseItemAnimator {
private long lastRemoval;
private int removeCount;
public MyScaleInLeftAnimator() {
lastRemoval = 0;
removeCount = 0;
}
public MyScaleInLeftAnimator(Interpolator interpolator) {
mInterpolator = interpolator;
lastRemoval = 0;
removeCount = 0;
}
#Override protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setPivotX(holder.itemView, 0);
}
#Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
long time = System.currentTimeMillis();
long d = time - lastRemoval;
if (d < 100) {
removeCount++;
} else {
removeCount = 0;
}
lastRemoval = time;
ViewCompat.animate(holder.itemView)
.scaleX(0)
.scaleY(0)
.setDuration(getRemoveDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultRemoveVpaListener(holder))
.setStartDelay(removeCount * 100)
.start();
}
#Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setPivotX(holder.itemView, 0);
ViewCompat.setScaleX(holder.itemView, 0);
ViewCompat.setScaleY(holder.itemView, 0);
}
#Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.scaleX(1)
.scaleY(1)
.setDuration(getAddDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultAddVpaListener(holder))
.setStartDelay(getAddDelay(holder))
.start();
}
}
This is how I have done without using any libraries - by inserting delays in the loop to remove items & restore (if needed)
clearItemsView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final List<LineItem> lineItemsCopy = new ArrayList<>(lineItems);
new Thread(new Runnable() {
#Override
public void run() {
for (int i=0; i<lineItemsCopy.size(); i++) {
runOnUiThread(new Runnable() {
#Override
public void run() {
salesOrderItemListAdapter.removeItem(0);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Snackbar snackbar = Snackbar.make(coordinatorLayout, getString(R.string.items_cleared_message), Snackbar.LENGTH_LONG)
.setAction(getString(R.string.label_undo), new View.OnClickListener() {
#Override
public void onClick(View v) {
new Thread(new Runnable() {
#Override
public void run() {
for (int i=0; i<lineItemsCopy.size(); i++) {
final int finalI = i;
runOnUiThread(new Runnable() {
#Override
public void run() {
salesOrderItemListAdapter.restoreItem(lineItemsCopy.get(finalI), 0);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}).setActionTextColor(Color.YELLOW);
snackbar.show();
}
});

ProgressDialog in Android not appearing (incorrect thread handling?)

I am trying to implement a radio player (using shoutcast streams) for android. What I want to do is, while the radio stream loads in the player, the UI displays a spinning wheel animation. On successful loading (as soon as the song starts playing) the animation disappears.
Here is the code that I am using.
PlayStopStreamingButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Thread initializer = new Thread(new Runnable() {
#Override
public void run() {
Looper.myLooper();
Looper.prepare();
progressDialog = ProgressDialog.show(RadioPlayerActivity.this, "", "Selecting Radio Station",
true);
JukefoxApplication.getHandler().post(new Runnable() {
#Override
public void run() {
radioPlayerEventListener.onPlayStopStreamingButtonClicked();
progressDialog.dismiss();
}
});
}
});
initializer.start();
}
});
I don't get any spinning animation. I am almost certain that my mistake lies in incorrect handling of threads. If someone could out the correct way, I would be grateful.
EDIT, this seems to work:
PlayStopStreamingButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
progressDialog = ProgressDialog.show(RadioPlayerActivity.this, "", "Selecting Radio Station", true);
Thread initializer = new Thread(new Runnable() {
#Override
public void run() {
radioPlayerEventListener.onPlayStopStreamingButtonClicked();
progressDialog.dismiss();
}
});
initializer.start();
}
});
You need to show progress dialog on UI thread, see below:
PlayStopStreamingButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Thread initializer = new Thread(new Runnable() {
#Override
public void run() {
Looper.myLooper();
Looper.prepare();
RadioPlayerActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog = ProgressDialog.show(RadioPlayerActivity.this,
"", "Selecting Radio Station", true);
}
});
JukefoxApplication.getHandler().post(new Runnable() {
#Override
public void run() {
radioPlayerEventListener.onPlayStopStreamingButtonClicked();
RadioPlayerActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog.dismiss();
}
});
}
});
initializer.start();
}
});

Categories

Resources