Android ProgressBar started from non-Activity class - android

I am creating an android application with custom OnClickListener that's defined in it's own class.
The problem is when I want to create Indeterminate Progress Bar in title bar that will be started when the onClick method will be called.
I can't setProgressBarIntederminateVisibility from MyOnClickListener class because it's not the main activity, and I can't request getParent because it's not a activity.
public class MyOnClickListener implements OnClickListener {
private message;
public MyOnClickListener(Context context,TextView mstatus, TextView message) {
this.message=message;
}
#Override
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.next:
this.message.setText(GetValue.getNextValue());//during this operation I want progress bar to be spinning
Log.v("MyOnClickListener", "next pressed");
break;
case R.id.prev:
this.message.setText(GetValue.getPrevValue());
Log.v("MyOnClickListener","prev pressed");
break;
case R.id.last:
this.message.setText(GetValue.getlastvalue());
break;
default: break;
}
}
}
What Can I do?

Typically, you'd have this as an inner class of your Activity, which then has an implicit reference to your "outer" Activity class. As an alternative, you can of course pass a reference to your Activity when you construct your listener object.

In case it helps someone else, I have an activity that calls a non-activity class which downloads an xml data file from a server. I wanted to show a progress indicator. I did it like this (this isn't a complete implementation of the classes but should give a good idea):
/*Setup Activity Class */
public class MySetup extends Activity implements ThreadCompleteListener{
Button btnLogin, btnItems, btnConfig, btnStart;
ProgressDialog pDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.setup_activity);
//set up links to buttons, etc
}
public void onBtnClicked(View v){
Intent intent;
switch(v.getId()){
case R.id.btn_download_items:
final Context ctx = this;
startProgressIndicator();
//async read a list of items from a server
myList=ItemsList.getItemsListInstance(ctx);
btnConfig.setEnabled(true);
break;
//other options here
}
}
public void startProgressIndicator(){
pDialog = new ProgressDialog(this);
pDialog.setMessage("Downloading items...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
public void endProgressIndicator(){
pDialog.dismiss();
}
}
Then in my non-activity class - I do the download
public class ItemsList implements ThreadCompleteListener{
//create a list of MyItem
private ArrayList<MyItem> items = new ArrayList<MyItem>();
//create a single static instance
private static ItemsList itemsListInstance;
String baseUrl; //to connect to webservice
Context ctx;
public ItemsList(Context context){
readJSONFeed("http://someurl/", context);
ctx=context;
}
public void readJSONFeed(String theURL, Context context) {
//Read JSON string from URL
final Context ctx = context;
setBaseUrl();
//NotifyThread is a class I found in a Stack Overflow answer
//that provides a simple notification when the async process has completed
NotifyThread getItemsThread = new NotifyThread(){
#Override
public void doRun(){
try {
//do the JSON read stuff here...
}catch (Exception e) {
}
};
getItemsThread.addListener(this);
getItemsThread.start();
}
//Overload the NotifyThread method
public void notifyOfThreadComplete(final Thread thread){
//CALL the method in the calling activity to stop the progress indicator
((MySetup)ctx).endProgressIndicator();
}
public static AuctionItemsList getItemsListInstance(Context context) {
if (itemsListInstance == null){
itemsListInstance = new itemsList(context);
}
return itemsListInstance;
}
}

Related

Android: How to implement Asynctask correctly for this below class?

Below is a login activity which connects with the server to perform login operation, so for this to do in Background thread how to use Asynctask's methods correctly?
I am new to android and not used Asynctask before, but I have seen tutorials still couldn't do it myself
//public class LoginActivity extends AppCompatActivity extends Asynctask shows some error
Edit: error is here
//public class LoginActivity extends AsyncTask extends
AppCompatActivity{ ( { expected)
public class LoginActivity extends AppCompatActivity{
private TextView tvLFS, tvOr;
private Button btnLog;
private EditText etUn, etPw;
private static final String TAG = "LoginActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//remove action bar
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
//change font of the heading
tvLFS = (TextView) findViewById(R.id.tvHeadingLFS);
Typeface typeface =
Typeface.createFromAsset(getAssets(),
"fonts/futuramediumitalicbt.ttf");
tvLFS.setTypeface(typeface);
init();
}
private void init() {
tvLFS = (TextView) findViewById(R.id.tvHeadingLFS);
tvOr = (TextView) findViewById(R.id.tvOR_LOGIN_USING);
btnLog = (Button) findViewById(R.id.btnLogin);
etUn = (EditText) findViewById(R.id.etUName);
etPw = (EditText) findViewById(R.id.etPass);
/* SharedPreferences pref = getSharedPreferences("ActivityPREF",
Context.MODE_PRIVATE);
SharedPreferences.Editor edt = pref.edit();
edt.putBoolean("activity_executed", true);
edt.commit();*/
btnLog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
final String usname = etUn.getText().toString();
final String uspass = etPw.getText().toString();
final LoginRequest loginRequest = new LoginRequest();
loginRequest.setClientType("mobile");
loginRequest.setMsService("login");
loginRequest.setMsServiceType("user-management");
List<LoginRequest.MsDataLogin> msDataLogList = new
ArrayList<>();
LoginRequest.MsDataLogin msData =
loginRequest.getMsDAtaLoginInstance();
msData.setUserName(usname);
msData.setUserPass(uspass);
msDataLogList.add(msData);
loginRequest.setMsData(msDataLogList);
RestClient.getApiInterface().postData(loginRequest).enqueue(new
ResponseResolver<LoginResponse>(LoginActivity.this) {
#Override
public void success(LoginResponse loginResponse) {
if (loginResponse.getErrorCode().equals("0"))
{
Toast.makeText(LoginActivity.this,
"Logged-in successfully!!", Toast.LENGTH_SHORT).show();
Intent in = new
Intent(getApplicationContext(), MainActivity.class);
startActivity(in);
finish();
} else
if(loginResponse.getErrorCode().equals("1")){
Toast.makeText(LoginActivity.this, "No
account found!! Please register", Toast.LENGTH_LONG).show();
}
}
#Override
public void failure(APIError error) {
Log.d(TAG, "failure: error--
"+error.getMessage());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Based on you edit, you're trying to extend two classes? Well, (I think) that's not possible in Java ...
Back to your question about AsyncTask. AsynTask are made to make task outside de Main Thread/UI Thread, for some scenarios (Ex.: the basic, not lock the UI while doing some work), for that reason you can't interact with the UI in a AsyncTask or even mix both things (is possible in some cases, but not recommended).
So you need to extends AsyncTask in other class than your view/activity (another Class.java or nested/internal class), example below:
public class MyAsyncTask extends AsyncTask<ParameterType, ProgressType, ReturnType> {
//Example to demonstrate UI interation
private IView view;
public MyAsyncTask(IView view) {
this.view = view;
}
#Override
protected ReturnType doInBackground(ParameterType... params) {
// do and update the work
return new ReturnType(); // work is done, return the result
}
// Override this method if you need to do something after the AsyncTask has finished (based on the return). Here you can interact with the UI too.
#Override
protected void onPostExecute(ReturnType o) {
// Example of UI interaction
view.updateUI(o);
}
}
If you don't need Parameters, Returns or update the progress of your AsyncTask, you can use the 'Void' type in place ParameterType, ProgressType or ReturnType.
Then you can create a intance of MyAsyncTask in other classes (Ex.: your activity) an call ‘execute()’ method to start the AsyncTask.
public class Foobar extends AppCompatActivity implements IView {
... code ...
MyAsyncTask fooTask = new MyAsyncTask(this); // Foobar class needs to implement IView interface
fooTask.execute(parameters); // execute AsyncTask with 'parameters'
... code ...
}
Based on your code you're trying to make a Network call. So you need need migrate your network call to inside 'doInBackground' method, and call the next activity (or show the error) in the 'onPostExecute'.
I not very familiar with your implementation (RestClient, ResponseResolver), but I think you can use Retrofit/Jackson libraries for a more solid solution. They are not very difficult to understand and makes Network calls easier.
In the references below there are other alternatives that you can use instead of a AsyncTask.
Here is some references:
https://developer.android.com/reference/android/os/AsyncTask.html
https://developer.android.com/guide/components/processes-and-threads.html
Good coding.

how to get object reference from other activity [android]

I have two major classes in my project. The first is for creating the connection between the client and the server. The second is for switching between activities.
first:
public class MyActivity extends Activity{
private ListView mList;
private ArrayList<String> arrayList;
private MyCustomAdapter mAdapter;
public TCPClient mTcpClient;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
boolean flag = getIntent().getBooleanExtra("flag",false);
arrayList = new ArrayList<String>();
final EditText editText = (EditText) findViewById(R.id.editText);
Button send = (Button)findViewById(R.id.send_button);
Button menu = (Button)findViewById(R.id.button1);
if (flag == true)
{
//relate the listView from java to the one created in xml
mList = (ListView)findViewById(R.id.list);
mAdapter = new MyCustomAdapter(this, arrayList);
mList.setAdapter(mAdapter);
new connectTask().execute("");
Intent myIntent = new Intent(MyActivity.this,Menu.class);
startActivity(myIntent);
}
send.setOnClickListener(new View.OnClickListener() {
// #Override
public void onClick(View view) {
String message = editText.getText().toString();
//clean the listView to 1 item
if (message.equals("clean"))
{
arrayList.removeAll(arrayList);
mList.removeAllViewsInLayout();
}
//add the text in the arrayList
arrayList.add("c: " + message);
//sends the message to the server
if (mTcpClient != null) {
mTcpClient.sendMessage(message);
}
//refresh the list
mAdapter.notifyDataSetChanged();
editText.setText("");
}
});
//change Activity to live screen mode (live)
menu.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent myIntent = new Intent(MyActivity.this, Menu.class);
startActivity(myIntent);
}
});
}
public class connectTask extends AsyncTask<String,String,TCPClient> {
#Override
protected TCPClient doInBackground(String... message) {
//we create a TCPClient object and
mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
// #Override
//print the message as an Item
public void messageReceived(String message) {
//this method calls the onProgressUpdate
publishProgress(message);
}
});
mTcpClient.run();
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
//in the arrayList we add the messaged received from server
arrayList.add(values[0]);
// notify the adapter that the data set has changed. This means that new message received
// from server was added to the list
mAdapter.notifyDataSetChanged();
}
}
}
the object TCPClient mTcpClient is the major factor in my app. I use it communicate with the server. In addition, even if I switch between activities it is still running properly so I still get info from server even though I am not in that activity.
second:
public class Menu extends Activity
{
public MyActivity myActivity;
public TCPClient mtcp;
protected void onCreate(Bundle savedInstanceState, MyActivity myActivity)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.menu);
ImageView action = (ImageView) findViewById(R.id.imageView1);
action.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event)
{
// here I would like to use mTcpClient object mentioned in the first class
return false;
}
});
}
Basically what I need is a help on how to create in the second class reference to the object mTcpClient that is described in the first class.
You are doing it wrong. If you want to use TcpClient class regardless of context it should NOT be related to first Activity. What you should do is to use singleton pattern:
class TcpClient {
protected static TcpClient mInstance = null;
public TcpClient() {
// your init code...
}
public static TcpClient getInstance() {
if( mInstance == null ) {
mInstance = new TcpClient();
}
return mInstance;
}
...
}
and then, whenever you want to use TcpClient you just do:
TcpClient client = TcpClient.getInstance();

How to communicate with activity after long task because activity was destroyed

I have created simple application with login feature. I have created separate task for do the login into server called LoginTask and a listener class called LoginListener.
public interface LoginListener {
public void onLoginComplete();
public void onLoginFailure(String msg);
}
public class LoginTask extends AsyncTask<String, Void, Boolean>{
private final LoginListener listener;
private final Context c;
private String msg;
public LoginTask(final Context c, final LoginListener listener) {
this.c = c;
this.listener = listener;
}
#Override
protected Boolean doInBackground(String... args) {
// loging in to server
//return true if success
}
#Override
protected void onPostExecute(Boolean status) {
if(!status){
if(listener != null) listener.onLoginFailure(msg);
return;
}
// the problem is here, listener is null, because activity/fragment destroyed
if(listener != null) listener.onLoginComplete();
}
}
I executed LoginTask from LoginFragment. The LoginFragment implements LoginListener.
public class LoginFragment extends Fragment implements LoginListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.frg_login, container, false);
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
doInitView();
};
private void doInitView(){
Button loginButton = (Button) getActivity().findViewById(R.id.login_btn);
Button regButton = (Button) getActivity().findViewById(R.id.toreg_btn);
ButtonListener listener = new ButtonListener();
loginButton.setOnClickListener(listener);
regButton.setOnClickListener(listener);
}
private void doLogin(){
Activity activity = getActivity();
EditText emailText = (EditText)activity.findViewById(R.id.login_email);
EditText pwdText = (EditText)activity.findViewById(R.id.login_pwd);
String email = emailText.getText().toString().trim();
String pwd = pwdText.getText().toString().trim();
if(StringUtil.isAnyNull(email, pwd)){
Popup.showMsg(getActivity(), "Silahkan lengkapi data", Popup.SHORT);
return;
}
savedEmail = email;
savedPwd = pwd;
String url = getActivity().getResources().getString(R.string.url_login);
Popup.showLoading(getActivity(), "Login", "Please wait...");
LoginTask task = new LoginTask(getActivity(), this);
task.execute(url, email, pwd);
}
private final class ButtonListener implements OnClickListener{
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.login_btn:
doLogin();
break;
case R.id.toreg_btn:
doToRegister();
break;
case R.id.demo_btn:
doDemo();
break;
}
}
}
#Override
public void onLoginComplete() {
// getActivity() is null
((MainActivity)getActivity()).gotoMain();
}
#Override
public void onLoginFailure(String msg) {
}
}
Because of the login task takes time, sometime the device light turn off before the task was finished so activity was destroyed. This caused the task failed to call the listener(fragment). How to solve this problem?
Thanks
AsyncTask should be used for tasks that take a bit longer and return a result into the current activity. However it's not intended for really long running tasks or for those cases where you want to evaluate its results even if the activity has been destroyed. You might consider using a Service here. In any case you shouldn't do updates in onPostExecute() anymore cause the activity context might be gone (see Doctoror Drive's post). Having that service in place, you can either send an Intent or a Broadcast event to the system. Then do the further processing in that intent activity / broadcast receiver.
You can cancel the asynctask in onDestroy() of your LoginActivity.
Override onCancelled() of the asynctask. When the activity is destroyed, a call to onCancelled() will be made instead of onPostExecute()
Here you can avoid a call back to the LoginActivity.
You should use Service or IntentService. because AsyncTask does not record any variables or context of Activity. When you finish login task launch PendingIntent or startActivity(intent). This can be best practice of Android. This way you never get exception.
In onLoginComplete and onLoginFailure check if the fragment is still attached to the activity. If not, do nothing.
#Override
public void onLoginComplete() {
if (isAdded() && !isRemoving() && !isDetached()) {
((MainActivity)getActivity()).gotoMain();
}
}

how to close custom dialog class in android

I have a problem with closing a custom dialog. I have two classes
class 1-> AndroidHTMLActivity
class 2-> CustomizeDialog
In my AndroidHTMLActivity I use java interface which is call from javascript, in this class i call CustomizeDialog
public class AndroidHTMLActivity extends Activity {
WebView myBrowser;
setContentView(R.layout.main);
myBrowser = (WebView)findViewById(R.id.mybrowser);
myBrowser.addJavascriptInterface(new MyJavaScriptInterface(this), "AndroidFunction");
myBrowser.getSettings().setJavaScriptEnabled(true);
myBrowser.loadUrl("file:///android_asset/mypage.html");
}
public class MyJavaScriptInterface {
Context mContext;
MyJavaScriptInterface(Context c) {
mContext = c;
}
public void openAndroidDialog(){
CustomizeDialog customizeDialog = new CustomizeDialog(mContext);
customizeDialog.show();
}
CustomizeDialog .java
public class CustomizeDialog extends Dialog {
Context ctx ;
public CustomizeDialog(Context context) {
super(context);
ctx = context;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
MyThread downloadThread = new MyThread();
downloadThread.start();
}
public class MyThread extends Thread {
#Override
public void run() {
try {
handler.post(new MyRunnable());
}
}
}
static public class MyRunnable implements Runnable {
public void run() {
// here i want to close this customized dialog
}
}
Here i can't use finish() method, I want to close the customized dialog box via the thread. Anyone has any idea about this?
Well I know this question is asked in the past and maybe already answered but haven't shared the correct answer but I still want to share this since I also got the same problem. Well here's what I did.
1st create the base class let say and create a static declaration for dialog.
public class Dialogs {
static Dialog dialog;
}
2nd is to put your custom dialog.
public void customDialog(Context context){
dialog = new Dialog(context);
dialog.setContentView(R.layout.dialog_login);
dialog.setTitle(title);
//... other parts here
dialog.show();
}
then the dialog dismiss:
public static void dismissDialog(){
dialog.dismiss();
}
and on the other class to close the currect customDialog just call
Dialogs.dismissDialog();
That's it. :) Hope it helps.
close it with outside handler like this
App.HANDLER.post(new Runnable() {
#Override
public void run() {
dismiss();
cancel();
}
});
App is a application class

AsyncTask : passing value to an Activity (onCreate method )

Update1
activity:
public Integer _number = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
if (_number >0)
{
Log.d("onSuccessfulExecute", ""+_number);
}
else
{
Log.d("onSuccessfulExecute", "nope empty songs lists");
}
}
public int onSuccessfulExecute(int numberOfSongList) {
_number = numberOfSongList;
if (numberOfSongList >0)
{
Log.d("onSuccessfulExecute", ""+numberOfSongList);
}
else
{
Log.d("onSuccessfulExecute", "nope empty songs lists");
}
return numberOfSongList;
}
end Update1
UPDATE: AsynchTask has its own external class.
How to pass an value from AsyncTask onPostExecute()... to activity
my code does returning value from onPostExecute() and updating on UI but i am looking for a way to set the activity variable (NumberOfSongList) coming from AsynchTask.
AsyncTask class:
#Override
public void onPostExecute(asynctask.Payload payload)
{
AsyncTemplateActivity app = (AsyncTemplateActivity) payload.data[0];
//the below code DOES UPDATE the UI textView control
int answer = ((Integer) payload.result).intValue();
app.taskStatus.setText("Success: answer = "+answer);
//PROBLEM:
//i am trying to populate the value to an variable but does not seems like the way i am doing:
app.NumberOfSongList = payload.answer;
..............
..............
}
Activity:
public Integer NumberOfSongList;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Several UI Code
new ConnectingTask().execute();
Log.d("onCreate", ""+NumberOfSongList);
}
What about using a setter method? e.g.
private int _number;
public int setNumber(int number) {
_number = number;
}
UPDATE:
Please look at this code. This will do what you're trying to accomplish.
Activity class
public class TestActivity extends Activity {
public int Number;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
Button btnDisplay = (Button) findViewById(R.id.btnDisplay);
btnDisplay.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast toast = Toast.makeText(v.getContext(), "Generated number: " + String.valueOf(Number), Toast.LENGTH_LONG);
toast.show();
}
});
new TestTask(this).execute();
}
}
AsyncTask class
public class TestTask extends AsyncTask<Void, Void, Integer> {
private final Context _context;
private final String TAG = "TestTask";
private final Random _rnd;
public TestTask(Context context){
_context = context;
_rnd = new Random();
}
#Override
protected void onPreExecute() {
//TODO: Do task init.
super.onPreExecute();
}
#Override
protected Integer doInBackground(Void... params) {
//Simulate a long-running procedure.
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, e.getMessage());
}
return _rnd.nextInt();
}
#Override
protected void onPostExecute(Integer result) {
TestActivity test = (TestActivity) _context;
test.Number = result;
super.onPostExecute(result);
}
}
Just a word of caution: Be very careful when attempting to hold a reference to an Activity instance in an AsyncTask - I found this out the hard way :). If the user happens to rotate the device while your background task is still running, your activity will be destroyed and recreated thus invalidating the reference being to the Activity.
Create a listener.
Make a new class file. Called it something like MyAsyncListener and make it look like this:
public interface MyAsyncListener() {
onSuccessfulExecute(int numberOfSongList);
}
Make your activity implement MyAsyncListener, ie,
public class myActivity extends Activity implements MyAsyncListener {
Add the listener to the constructor for your AsyncTask and set it to a global var in the Async class. Then call the listener's method in onPostExecute and pass the data.
public class MyCustomAsync extends AsyncTask<Void,Void,Void> {
MyAsyncListener mal;
public MyCustomAsync(MyAsyncListener listener) {
this.mal = listener;
}
#Override
public void onPostExecute(asynctask.Payload payload) {
\\update UI
mal.onSuccessfulExecute(int numberOfSongList);
}
}
Now, whenever your AsyncTask is done, it will call the method onSuccessfulExecute in your Activity class which should look like:
#Override
public void onSuccessfulExecute(int numberOfSongList) {
\\do whatever
}
Good luck.

Categories

Resources