Custom onClick attribute is not working - android

I created a compound view consisting of one TextView and one "Buy" Button.
For my compound view, I made a custom attribute called onBuyClick. It is supposed to serve the same purpose as the View's onClick attribute, but have the listener not on the whole view but only on the button. So, the first thing I did is copy from the View class what the onClick attribute does. And it worked! (with minor adaptations).
But as soon as I add btnBuy.setOnClickListener(new OnClickListener() {, it becomes unable to find the method I want it to get and throws IllegalStateException (The second one, where NoSuchMethodException is being caught).
Here is the piece that executed once the is an onBuyClick attribute:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
btnBuy.setOnClickListener(new OnClickListener() { //As soon as "btnBuy." is added, everything crashes once the button is pressed.
private Method mHandler;
public void onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
} catch (NoSuchMethodException e) {
int id = getId();
String idText = id == NO_ID ? "" : " with id '"
+ getContext().getResources().getResourceEntryName(
id) + "'";
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity "
+ getContext().getClass() + " for onClick handler"
+ " on view " + this.getClass() + idText, e);
}
}
try {
mHandler.invoke(getContext(), this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non "
+ "public method of the activity", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not execute "
+ "method of the activity", e);
}
}
});
}
and here are the variables defined outside this piece: btnBuy, the "Buy" Button in my compound view, and a is a TypedArray containing all my attributes.
Can someone please help me?

Related

Definning a reference to method attribute for custom view

I want my custom view to have an attribute that can be set via XML that defines a callback for some custom behaviour that this view would support.
I know that i can tweak a simple string to be such using reflections, but it exists in the android API as Button has android:onClick so i wonder if this is something i can get out of the box instead of reinventing the wheel.
Anyone familiar with how it is done or do i must go ahead and implement it on my own using refelections?
seeing the android source code, looks as they did the same as i was going to :
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new OnClickListener() {
private Method mHandler;
public void More ...onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
} catch (NoSuchMethodException e) {
int id = getId();
String idText = id == NO_ID ? "" : " with id '"
+ getContext().getResources().getResourceEntryName(
id) + "'";
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity "
+ getContext().getClass() + " for onClick handler"
+ " on view " + View.this.getClass() + idText, e);
}
}
try {
mHandler.invoke(getContext(), View.this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non "
+ "public method of the activity", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not execute "
+ "method of the activity", e);
}
}
});
}
So this i guess is the only way...

Add validation so that EditText fields cannot be null

So I'm trying to add validation onto my btnSubmit which will check that all editText fields have some sort of data in before storing into the array. Would anyone be able to help?
if (btnSubmit == null) throw new AssertionError();
btnSubmit.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(editMod.getText().length()==0){
editMod.setError("Please fill in all fields");
}
else if(editRoom.getText().length()==0){
editRoom.setError("Please fill in all fields");
}
if (editMod.getText() != null) {
strModule = editMod.getText().toString();
}
if (editRoom.getText() != null) {
strRoomInfo = editMod.getText().toString();
}
inputData = strDay + " " + strTime + " " + strDuration + " " + strType + " " + strModule + " " + strRoomInfo;
parent.addItem(inputData);
try {
writeFile();
} catch (Exception e) {
Log.e("Writing to file Failed.", " ");
}
}
});
}
What's editMod here? EditText or TextInputLayout?
For this current code, here is the updated version:
if (btnSubmit == null) throw new AssertionError();
btnSubmit.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(editMod.getText().toString().isEmpty()){
editMod.setError("Please fill in all fields");
}
else if(editRoom.getText().toString().isEmpty()){
editRoom.setError("Please fill in all fields");
}
if (editMod.getText().toString() != null) {
strModule = editMod.getText().toString();
}
if (editRoom.getText().toString() != null) {
strRoomInfo = editMod.getText().toString();
}
inputData = strDay + " " + strTime + " " + strDuration + " " + strType + " " + strModule + " " + strRoomInfo;
parent.addItem(inputData);
try {
writeFile();
} catch (Exception e) {
Log.e("Writing to file Failed.", " ");
}
}
});
}
Update your question and add xml code plus the rest of Java Code.
Try this code:
if (TextUtils.isEmtpy(<Your EditText here>.getText().toString())) {
//Empty
}

Put a List<String> in a Bundle

I have an IntentService that goes connects to a website and creates a list with parsed HTML via JSoup. I now need to pass that list back in a Bundle and I'm not sure how to do it. Here is my code for the IntentService:
public class NewerService extends IntentService {
public NewerService() {
super("NewerService");
// TODO Auto-generated constructor stub
}
#Override
protected void onHandleIntent(Intent intent) {
ResultReceiver rec = intent.getParcelableExtra("receiverTag");
String playersName = intent.getStringExtra("Player");
List<String> list = new ArrayList<String>();
Document doc = null;
try {
doc = Jsoup.connect("http://espn.go.com/nhl/team/stats/_/name/phi/philadelphia-flyers").get();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (Element table : doc.select("table.tablehead")) {
for (Element row : table.select("tr")) {
Elements tds = row.select("td");
if (tds.size() > 6) {
String a = tds.get(0).text() + ":" + " Games Played: "+
tds.get(1).text()+"," + " GOALS: " + tds.get(2).text()+"," +
" ASSISTS: " + tds.get(3).text() + " POINTS: " +
tds.get(4).text() + " PLUS/MINUS: " + tds.get(5).text() + "
PIM: " + tds.get(6).text();
list.add(a); // Add the string to the list
}
}
}
}
I appreciate any help in advance. Thank you
Have you tried using Bundle.putStringArrayList or Intent.putStringArrayListExtra?
If you mean you don't know how to start an activity from a service, try this question.

android list refreshing only from one void and not from another

this is quite odd to me: in my project (which by the way is derived from Yaxim, so it's a chat app for android), I'm trying to implement writing notification through the messageeventlistener. I do receive the notification event. To make it appear in the listview (which is in another class) I'm adding a row in the database, as if it were a message (only the text is "..."). I've added a counter and the row is properly inserted in the db (subsequently the row is deleted from the db when the actual message comes). The notifychanges is also called properly. The strange thing is that despite all of this the row doesn't appear in the listview. If I move the procedure a few lines below, on the packetlistener for the regular messages, it gets inserted. However I cannot leave it like that because the packetlistener doesn't receive properly all composing notifications, so it doesn't work all the time for that purpose. Also, while the writing notification is properly inserted into the db, if I keep the procedure in the messageEventNotificationListener() the writing notification message does not get deleted when the actual message arrives (and the iCount keeps increasing); if I put the procedure in the packetListener it does (hence iCount equals 0 after the message is pushed into the db)
This is the messageeventlistener:
private void messageEventNotificationListener(){
// Create a MessageEventManager
if (mMessageEventManager == null)
mMessageEventManager = new MessageEventManager(mXMPPConnection);
// Add the listener that will react to the event notifications
mMessageEventManager.addMessageEventNotificationListener(new MessageEventNotificationListener() {
public void deliveredNotification(String from, String packetID) {
Log.d(TAG, "The message has been delivered (" + from + ", " + packetID + ")");
}
public void displayedNotification(String from, String packetID) {
Log.d(TAG, "The message has been displayed (" + from + ", " + packetID + ")");
}
public void composingNotification(String from, String packetID) {
Log.d(TAG, "The message's receiver is composing a reply (" + from + ", " + packetID + ")");
//controlla se l'utente non sta giĆ  scrivendo
String selection = ChatConstants.JID + " = '" + from + "' AND " +
ChatConstants.DELIVERY_STATUS + " = " + ChatConstants.DS_WRITING;
Cursor cursor = mContentResolver.query(ChatProvider.CONTENT_URI,
new String[] { "count(" + ChatConstants._ID + ")" },
selection, null, null);
cursor.moveToFirst();
int iCount = cursor.getInt(0);
//if (iCount == 0)
//{
addChatMessageToDB(ChatConstants.INCOMING, from, "...", ChatConstants.DS_WRITING, System.currentTimeMillis(), packetID);
//}
}
public void offlineNotification(String from, String packetID) {
Log.d(TAG, "The message's receiver is offline (" + from + ", " + packetID + ")");
}
public void cancelledNotification(String from, String packetID) {
Log.d(TAG, "The message's receiver cancelled composing a reply (" + from + ", " + packetID + ")");
}
});
}
and here is the packetlistener:
private void registerMessageListener() {
// do not register multiple packet listeners
if (mPacketListener != null)
mXMPPConnection.removePacketListener(mPacketListener);
PacketTypeFilter filter = new PacketTypeFilter(Message.class);
mPacketListener = new PacketListener() {
public void processPacket(Packet packet) {
try {
if (packet instanceof Message) {
Message msg = (Message) packet;
String chatMessage = msg.getBody();
DeliveryReceipt dr = (DeliveryReceipt)msg.getExtension("received", DeliveryReceipt.NAMESPACE);
if (dr != null) {
Log.d(TAG, "got delivery receipt for " + dr.getId());
changeMessageDeliveryStatus(dr.getId(), ChatConstants.DS_DELIVERED);
}
if (chatMessage == null)
return;
if (msg.getType() == Message.Type.error) {
chatMessage = "<Error> " + chatMessage;
}
long ts;
DelayInfo timestamp = (DelayInfo)msg.getExtension("delay", "urn:xmpp:delay");
if (timestamp == null)
timestamp = (DelayInfo)msg.getExtension("x", "jabber:x:delay");
if (timestamp != null)
ts = timestamp.getStamp().getTime();
else
ts = System.currentTimeMillis();
String fromJID = getJabberID(msg.getFrom());
//elimina il messaggio "writing" se esiste
deleteWritingChatMessageFromDB(fromJID);
if (msg.getExtension("request", DeliveryReceipt.NAMESPACE) != null) {
// got XEP-0184 request, send receipt
sendReceipt(msg.getFrom(), msg.getPacketID());
}
addChatMessageToDB(ChatConstants.INCOMING, fromJID, chatMessage, ChatConstants.DS_NEW, ts, msg.getPacketID());
mServiceCallBack.newMessage(fromJID, chatMessage);
}
}
} catch (Exception e) {
// SMACK silently discards exceptions dropped from processPacket :(
Log.e(TAG, "failed to process packet:");
e.printStackTrace();
}
};
mXMPPConnection.addPacketListener(mPacketListener, filter);
}
and finally the insert and delete method
private void addChatMessageToDB(int direction, String JID,
String message, int delivery_status, long ts, String packetID) {
ContentValues values = new ContentValues();
values.put(ChatConstants.DIRECTION, direction);
values.put(ChatConstants.JID, JID);
values.put(ChatConstants.MESSAGE, message);
values.put(ChatConstants.DELIVERY_STATUS, delivery_status);
values.put(ChatConstants.DATE, ts);
values.put(ChatConstants.PACKET_ID, packetID);
Uri noteUri = mContentResolver.insert(ChatProvider.CONTENT_URI, values);
//mContentResolver.notifyChange(noteUri, null);
}
private void deleteWritingChatMessageFromDB(String JID) {
int count = mContentResolver.delete(ChatProvider.CONTENT_URI,
ChatConstants.JID + " = ? AND " + ChatConstants.MESSAGE + " = ? AND " + ChatConstants.DELIVERY_STATUS + " = ?", new String[] { JID, "...", "3" });
debugLog("deleteWritingChatMessageEntryFromDB: Deleted " + count + " entries");
}
both these methods get called into the ChatProvider class and end with
getContext().getContentResolver().notifyChange(url, null);
Turns out the problem was different: I posting this just in case someone else runs in the same issue. The message was infact inserted into the db, but while the packetlistener returns only the jid, the messageeventlistener returns the jid with the resource at the end as well, which needed to be taken away before the insert. Done that, the message appeared in the list. Also: the code I posted is not completely correct to handle the messageEvent. The listeners need to be called from inside the packetListener directly.

Updating UI from AsyncTask after exiting and re-entering app

I am currently working on an app that runs an AsyncTask in the background and updates the UI with a progressbar. The progress bar works fine when the app is running, however, when the user exits the app and re-enters, the AsyncTask is still running in the background, but the progress bar won't update. It's as if the AsyncTask has detached itself from the activity. Does anyone have any ideas what might be causing this such as general rules involved with AsyncTasks. I can provide code if needed, but it is rather lengthy, so just let me know what parts you would need to see. I should also note that the AsyncTask does complete, I can tell this because it uploads a database to the server when it finishes.
Here is the code:
public class BackgroundAsyncTask extends AsyncTask {
int myProgress;
#Override
protected void onPostExecute(Void result) {
((TextView) findViewById(R.id.tv1)).setText("");
Cursor cur = sql3.query("videohashes", null, null, null, null,
null, null);
cur.moveToFirst();
while (!cur.isAfterLast()) {
Cursor curFrame = sql3.query("table_" + cur.getString(2), null,
null, null, null, null, null);
curFrame.moveToFirst();
((TextView) findViewById(R.id.tv1)).append("\nPath: "
+ cur.getString(1) + "\nHash: " + cur.getString(2)
+ "\nDate:" + cur.getString(3) + "\nSize: "
+ cur.getString(4) + " bytes\nResolution"
+ cur.getString(5) + "\nFormat: " + cur.getString(6)
+ "\nCodec: " + cur.getString(7) + "\nFPS: "
+ cur.getString(8) + "\n\nFirst Frame Info:\nType: "
+ curFrame.getString(1) + "\ncp_num: "
+ curFrame.getString(2) + "\ndp_num: "
+ curFrame.getString(3) + "\npts: "
+ curFrame.getString(4) + "\nqstride: "
+ curFrame.getString(5) + "\nsize: "
+ curFrame.getString(6) + "\nqp_stddev: "
+ curFrame.getString(7) + "\ncount: "
+ curFrame.getString(8) + "\nqp_avg: "
+ curFrame.getString(9) + "\n\n");
cur.moveToNext();
}
cur.close();
((Button) findViewById(R.id.btnSend)).setEnabled(true);
((Button) findViewById(R.id.btnStart)).setEnabled(true);
sql3.close();
sharedPreferences.edit().putString("lastVideoInfo", ((TextView) findViewById(R.id.tv1)).getText().toString()).commit();
sharedPreferences.edit().putBoolean("asyncTaskRunning", false).commit();
dateNow = new Date();
super.onPostExecute(result);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
totProgress = 0;
currVid = 0;
curProgress = 0;
}
#Override
protected void onProgressUpdate(Integer... values) {
progress.setProgress(values[0]);
}
#Override
protected Void doInBackground(Void... arg0) {
// Calculate total size of all files
for (String path : myFiles) {
totProgress += getFileSize(path);
}
progress.setMax(totProgress);
String strDB3File = getFilesDir().getPath() + "/VideoHashes.db3";
sql3 = SQLiteDatabase.openDatabase(strDB3File, null,
SQLiteDatabase.CREATE_IF_NECESSARY);
try {
String mysql = "CREATE TABLE IF NOT EXISTS videohashes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, path TEXT NOT NULL, hash TEXT NOT NULL, date TEXT NOT NULL, size INTEGER, resolution TEXT NOT NULL, codec TEXT NOT NULL, format TEXT NOT NULL, fps TEXT NOT NULL)";
sql3.execSQL(mysql);
} catch (SQLiteException e) {
// TODO: handle exception
}
for (String path : myFiles) {
try {
String hash = getMD5Checksum(path);
Cursor curFrame = sql3.query("videohashes",
new String[] { "hash" }, "hash=?",
new String[] { hash }, null, null, null);
if (!curFrame.moveToFirst()) {
ContentValues myInsertData = new ContentValues();
myInsertData.put("path", path);
myInsertData.put("hash", hash);
Date date = new Date();
myInsertData.put("date", dateFormat.format(date));
myInsertData.put("size", getFileSize(path));
naInit(path);
Log.i("VPMA", "After naInit");
int[] prVideoRes = naGetVideoResolution();
myInsertData.put("resolution", prVideoRes[0] + "x"
+ prVideoRes[1]);
String prVideoCodecName = naGetVideoCodecName();
myInsertData.put("codec", prVideoCodecName);
String prVideoFormatName = naGetVideoFormatName();
myInsertData.put("format", prVideoFormatName);
double prFps = naGetVideoFPS();
Log.i("VPMA", "fps: " + prFps);
myInsertData.put("fps", prFps);
Object[] prObjArray = naGetArray();
Log.i("VPMA", (String) prObjArray[0]);
String[] prStrArray = Arrays.copyOf(prObjArray,
prObjArray.length, String[].class);
Log.i("VPMA", "before frames");
try {
String mysql = "CREATE TABLE table_"
+ hash
+ " (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, cp_num TEXT NOT NULL, dp_num TEXT NOT NULL, pts TEXT NOT NULL, qstride TEXT NOT NULL, size TEXT NOT NULL, qp_stddev TEXT NOT NULL, count TEXT NOT NULL, qp_avg TEXT NOT NULL)";
sql3.execSQL(mysql);
} catch (SQLiteException e) {
// TODO: handle exception
}
for (String str : prStrArray) {
ContentValues myFrameInsertData = new ContentValues();
String[] strArr = str.split(",");
if (strArr.length == 9) {
String stddev = "", strCount = "", strQp_avg = "";
double sd, qp_avg, count = 0, sum = 0, sqrSum = 0;
try {
count = Integer.parseInt(strArr[6].trim());
sum = Integer.parseInt(strArr[7].trim());
sqrSum = Integer.parseInt(strArr[8].trim());
//sd = (sum * sum / count);
sd = (sqrSum - (sum*sum/count))/(count-1);//(sqrSum - sd) / (count - 1);
stddev = String.valueOf(sd);
qp_avg = sum / count;
strCount = String.valueOf(count);
strQp_avg = String.valueOf(qp_avg);
} catch (Exception e) {
Log.i("Error: ", "error converting values");
}
//Log.i("Java Code: ", "Sum: " + sum + " SqrSum: " + sqrSum + " Count: " + count);
//Log.i("Java Code: ", "StdDev: " + stddev + " qp_avg: " + strQp_avg);
myFrameInsertData.put("type", strArr[0]);
myFrameInsertData.put("cp_num", strArr[1]);
myFrameInsertData.put("dp_num", strArr[2]);
myFrameInsertData.put("pts", strArr[3]);
myFrameInsertData.put("qstride", strArr[4]);
myFrameInsertData.put("size", strArr[5]);
myFrameInsertData.put("qp_stddev", stddev);
myFrameInsertData.put("count", strCount);
myFrameInsertData.put("qp_avg", strQp_avg);
sql3.insert("table_" + hash, null,
myFrameInsertData);
}
}
sql3.insert("videohashes", null, myInsertData);
naClose();
}
curFrame.close();
currVid++;
curProgress += getFileSize(path);
publishProgress(curProgress);
Log.i("Progress", "CurrVid:" + currVid + " Max:"
+ progress.getMax());
} catch (Exception e) {
Log.i("File", "File not Found");
}
}
return null;
}
}
}
if (sharedPreferences.getBoolean("asyncTaskRunning", false) == false)
{
((Button) findViewById(R.id.btnStart)).setEnabled(false);
progress = (ProgressBar) findViewById(R.id.progressBar1);
text = (TextView) findViewById(R.id.tv1);
if (sharedPreferences.contains("lastVideoInfo"))
{
text.setText("Last Video Information Parsed " + "(" + dateFormat.format(dateNow) + "):\n\n" + sharedPreferences.getString("lastVideoInfo", ""));
((Button) findViewById(R.id.btnSend)).setEnabled(true);
}
else
{
text.setText("");
((Button) findViewById(R.id.btnSend)).setEnabled(false);
}
progress.setProgress(0);
myFiles = new ArrayList<String>();
new StartAsyncTask().execute();
}
}
When the Activity is destroyed, it loses its reference to the AsyncTask, as when the AsyncTask is created it is passed in a reference to the instance of the Activity that creates it. When the instance dies, the reference to the Activity becomes useless.
A better approach would be put the AsyncTask into a Service, set up the Service and set the AsyncTask running in the Service and bind your Activity to the Service.
Then when a new instance of the Activity is created (ie when the user re-enters the app), it can bind to the same Service instance that's already running and pass in a reference to its self to receive progress info.
Another advantage of this approach is that your Service can put a notification icon in the notification bar, which greatly reduces it chances of being killed by the system, the user could view progress at a glance, and even be notified of when the process is complete.
Allowing an AsyncTask to be cut loose from its owner (is the Activity) and trusting that it will complete what its doing is a pretty bad idea, and will probably have some unexpected results, lead to potential memory leaks etc.

Categories

Resources