In my Android Project, I try to use Transform API and Javasist to make an AOP framework.
But some class is transformed to an empty class and then I got the build error :
/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar] (Can't process class xxx/xxx/xxx/ABC.class
because the ABC.class get empty after javasist handle(I just load it with javasiit and then write back).
The code I write to modify class is :
public void weave(String path) {
println("Begin to weave androidClassPath = " + androidClassPath)
println("Begin to weave path = " + path)
pool.appendClassPath(path)
pool.appendClassPath(androidClassPath)
pool.importPackage("android.util.Log");
File dir = new File(path)
int indexOfPackage = path.length() + 1;
if (dir.isDirectory()) {
dir.eachFileRecurse { File file ->
String filePath = file.absolutePath
if (isWeavableClass(filePath)) {
println("Begin to inject filePath " + filePath)
int end = filePath.length() - 6 // .class = 6
String className = filePath.substring(indexOfPackage, end).replace(File.separator, '.')
CtClass clazz = pool.getCtClass(className)
if (clazz.isFrozen()) {
clazz.defrost()
}
boolean timeDebugClass = false;
boolean parameterDebugClass = false;
if(clazz.hasAnnotation(TimingDebug.class)) {
timeDebugClass = true;
}
if(clazz.hasAnnotation(ParameterDebug.class)) {
parameterDebugClass = true;
}
println "timeDebugClass = "+ timeDebugClass + " parameterDebugClass = " + parameterDebugClass
CtMethod[] methods = clazz.getDeclaredMethods();
for (CtMethod method : methods) {
boolean emptyMethod = method.isEmpty()
boolean isNativeMethod = Modifier.isNative(method.getModifiers());
println("method name = " + method + " emptyMethod " + emptyMethod + " isNativeMethod = " + isNativeMethod)
if (!emptyMethod && !isNativeMethod) {
if (method.hasAnnotation(ParameterDebug.class) || parameterDebugClass) {
weaveParameterDebugMethod(clazz, method)
}
if (method.hasAnnotation(TimingDebug.class) || timeDebugClass) {
weaveTimingDebugMethod(clazz, method)
}
}
}
CtConstructor[] constructors = clazz.getDeclaredConstructors();
for(CtConstructor constructor: constructors){
boolean emptyMethod = constructor.isEmpty()
println("constructor name = " + constructor + " emptyMethod " + emptyMethod)
if (!emptyMethod) {
if (constructor.hasAnnotation(ParameterDebug.class) || parameterDebugClass) {
weaveParameterDebugMethod(clazz, constructor)
}
if (constructor.hasAnnotation(TimingDebug.class) || timeDebugClass) {
weaveTimingDebugMethod(clazz, constructor)
}
}
}
clazz.writeFile(path)
clazz.detach()
}
}
}
}
Related
Hi I am new to clips rule engine and have to do following in clips in mobile app (Android/Kotlin). we are using CLIPS4android library as wrapper of JNI.
How to get output from class file, when the rule gets executed by invoking the clips.run()
class RulesTest(filepath: String?) {
private val clips: Environment
fun stop() {
clips.destroy()
}
/**
* Example of how to use "assert".
* #throws CLIPSError
*/
#Throws(CLIPSError::class)
fun assertItems(products: List<Item>) {
for (product in products) {
Log.d(tag, "(Product " + "(productId " + product.ProductId + ")" + "(uomid " + product.UOMId + " )" + "(quantity " + product.Quantity + " ))")
var InsertItem: String
InsertItem = "(Product " + "(productId " + product.ProductId + ")" + "(uomid " + product.UOMId + " )" + "(quantity " + product.Quantity + " ))"
clips.assertString(InsertItem)
}
}
fun run() {
clips.eval("(facts)");
clips.run()
}
companion object {
const val tag = "CLIPSProductRulesTest"
}
init {
clips = Environment()
clips.load(filepath)
Log.d(tag, "Loading .clp...\n\n")
clips.reset()
}
}
In the 0.4 version of CLIPSJNI, you can define an implementation of the Router class to capture the output and then you can do whatever your want with the output in the print method of the implementation.
import CLIPSJNI.*;
class Example
{
static class CaptureRouter implements Router
{
public int getPriority()
{
return 10;
}
public String getName()
{
return "grab";
}
public boolean query(
String logName)
{
if (logName.equals("wwarning") ||
logName.equals("werror") ||
logName.equals("wtrace") ||
logName.equals("wdialog") ||
logName.equals("wprompt") ||
logName.equals("wdisplay") ||
logName.equals("stdout"))
{ return true; }
return false;
}
public void print(
String routerName,
String printString)
{
System.out.print(printString);
}
public int getchar(
String routerName)
{
return -1;
}
public int ungetchar(
String routerName,
int theChar)
{
return -1;
}
public boolean exit(
int exitCode)
{
return true;
}
}
public static void main(String args[])
{
Environment clips;
clips = new Environment();
clips.addRouter(new CaptureRouter());
clips.build("(defrule hello => (printout t \"Hello World!\" crlf))");
clips.reset();
clips.run();
}
}
I am trying to make an android app for RFID card reader (i am not using NFC), for this i connect one high frquency RFID card reader through OTG cable and i am using EditText where card number is displaying. it is working fine, but problem is sometime it detects multiple time card number.
1- Any idea how can i resolve this (i cannot put size limit condition because card number length is not fixed)?
2- One more problem when i am using ultra high frequency card reader then card is showing different value, any idea how can i make an android app which supports both frequency card readers.
Rfid continue reading tag its not one time reading so from this repetition you have to manage to your side. Check its reading data or not if yes then ignore.Below the call Where implement Rfid methods and its reading data handle.
public class ReaderListener implements RfidEventListener {
private RfidReader reader;
private ReadInventory eventForm;
//private ItemTransfer eventFormitm;
private final ToneGenerator tg = new ToneGenerator(5, 100);
private boolean isEnabled;
private Map<String, Long> scannedItemsMap = new TreeMap<String, Long>();
public ReaderListener(ReadInventory frm, String make, String macid) throws ReaderConnectionException, LicenseExpiryException, IOException, GeneralSecurityException {
Log.d("Test1", " "+make.toString().trim()+" "+ macid.toString().trim());
reader = RfidFactory.getInstance().getRfidReader(make.toString().trim(), macid.toString().trim(),
PlatformConnector.AndroidConnector.getPlatformName());
Log.d("Test2", " "+reader+" ");
//reader.removeAllListeners();
reader.registerListener(this);
setEnabled(true);
this.eventForm = frm;
}
#Override
public void handleData(String tagid, int arg1, int arg2) {
synchronized (scannedItemsMap) {
if (!scannedItemsMap.containsKey(tagid)) {
System.out.println("got data............ :" + tagid);
scannedItemsMap.put(tagid, System.currentTimeMillis());
if (eventForm != null) {
eventForm.handleData(tagid);
//this.tg.startTone(25);
Log.d("tagid", " " + tagid + " ");
}
/*if (eventFormitm != null) {
eventFormitm.handleData(tagid);
}*/
} else if (scannedItemsMap.containsKey(tagid)) {
scannedItemsMap.put(tagid, System.currentTimeMillis());
}
}
}
#Override
public void handleError(String msg) {
if (eventForm != null) {
eventForm.handleError(msg);
}
}
#Override
public boolean isEnabled() {
return isEnabled;
}
#Override
public void setEnabled(boolean arg0) {
this.isEnabled = arg0;
}
public boolean startScan(int power,int speed) throws ReaderConnectionException {
if (reader != null && !reader.isScanning()) {
reader.setSession(RfidSession.ONE);
reader.setPower(power);
reader.setScanSpeed(speed);
reader.startScan();
}
return true;
}
public void stopScan() throws ReaderConnectionException {
if (reader.isScanning())
reader.stopScan();
}
public boolean isScanning() {
return reader.isScanning();
}
public boolean isConnected() {
return reader.isConnected();
}
public void removeAll() {
scannedItemsMap.clear();
}
public void remove(String tagid) {
scannedItemsMap.remove(tagid);
}
public int getBatteryLevel(){
try {
return reader.getBatteryLevel();
}catch (Exception e){
return 0;
}
}
#Override
public void handleReaderEvent(ReaderEvent arg0) {
// TODO Auto-generated method stub
}
}
Now Handle The data in activity ....
Show in below method.
public void handleData(final String tagid) {
// TODO Auto-generated method stub
try {
final String Code_samplecode = convertHexToString(tagid);
// Log.e("Name", "Itemcode" + Code_samplecode);
if (SampleCode.contains(Code_samplecode)&& !p_SampleCode.contains(Code_samplecode)) {
// Scann items count
tgf.startTone(25);
int ind_val = SampleCode.indexOf(Code_samplecode);
if (ind_val != -1) {
final String SampleNo1 = SampleNo.get(ind_val);
final String StyleNo1 = StyleNo.get(ind_val);
final String SampleCode1 = SampleCode.get(ind_val);
final String StyleCode1 = StyleCode.get(ind_val);
//final String CartStyleCode1 = CartStyleCode.get(ind_val);
// final String CartStyleNo1 = CartStyleNo.get(ind_val);
SampleNo.remove(ind_val);
StyleNo.remove(ind_val);
SampleCode.remove(ind_val);
StyleCode.remove(ind_val);
runOnUiThread(new Runnable() {
#Override
public void run() {
// p_Code.add(Code.get(ind_val));
p_SampleNo.add(SampleNo1);
p_StyleNo.add(StyleNo1);
p_SampleCode.add(SampleCode1);
p_StyleCode.add(StyleCode1);
//Code.remove(ind_val);
// adapter3.notifyDataSetChanged();
// adapter1.notifyDataSetChanged();
total_item.setAdapter(null);
adapter3 = new Adapter_for_Inventoryitem(ReadInventory.this,
StyleNo, SampleNo);
total_item.setAdapter(adapter3);
present_item.setAdapter(null);
adapter1 = new Adapter_for_Inventoryitem(ReadInventory.this,
p_StyleNo, p_SampleNo);
present_item.setAdapter(adapter1);
textvie.setText("Total " + p_SampleNo.size() + " Found Styles");
textvi.setText("Total " + SampleNo.size() + " Available Styles");
List<Inventory> c = db.get_all_data_INVENTARY_Query("SELECT * FROM Inventory_n WHERE SampleCode ='" + SampleCode1 + "'");
if (c.size() > 0) {
db.execute_query("INSERT INTO Inventory_p (Code, SampleNo,StyleNo,SampleCode,StyleCode,CartStyleNo,CartStyleCode) VALUES ('" + c.get(0).getCode() + "' , \"" +
c.get(0).getSampleNo() + "\" , \"" + c.get(0).getStyleNo() + "\" , '" +
c.get(0).getSampleCode() + "' , '" + c.get(0).getStyleCode() + "' , \"" +
c.get(0).getCartStyleNo() + "\" , '" + c.get(0).getCartStyleCode() + "')");
}
db.execute_query("DELETE FROM Inventory_n WHERE SampleCode ='" + SampleCode1 + "'");
}
});
}
} else {
if (!SampleCode.contains(Code_samplecode) && !p_SampleCode.contains(Code_samplecode)
&& !not_fount_items.contains(Code_samplecode)) {
tgn.startTone(20);
scanneditems_unkown = scanneditems_unkown + 1;
runOnUiThread(new Runnable() {
#Override
public void run() {
not_fount_items.add(Code_samplecode);
not_fount_items_txt.setText("Total " + not_fount_items.size() + " Unknown Styles");
}
});
}
}
runOnUiThread(new Runnable() {
#Override
public void run() {
scan_counts.setText("Total " + p_SampleNo.size() + " Scanned");
scan_counts_unknown.setText("Total " + scanneditems_unkown + " Unknown");
last_scanned_items.setText("Item : " + Code_samplecode);
}
});
} catch (Exception e) {
e.printStackTrace();
Message message = new Message();
message.what = READEREXCEPTION;
itemDetectedHandler.sendMessage(message);
}
}
You need to Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
//To get the characters (One By one)
event.getDisplayLabel();
}
And use Debounce (I'll recommend using RX Java) if the card numbers are of different length.
What debounce does is, it will wait for a particular amount of time after a keyevent happens, So you can concat the whole string and then use it.
I had some code to get a list of all the classes in a package that looked something like this:
try {
DexFile df = new DexFile(context.getPackageCodePath());
for (Enumeration<String> iter = df.entries(); iter.hasMoreElements();) {
String s = iter.nextElement();
}
} catch (IOException e) {
e.printStackTrace();
}
However this code has stopped working since I upgraded my Android Studio to version 2.0. I've found that the culprit is Instant Run. If I debug the app I can see that without instance run, the DexFile variable, df, contains a list of class names (over 4,000 of them). When Instant Run is turned on, I only get like 30 or so class names, and the classes that I'm looking for aren't there. I have a feeling it has something to do with multi dex but I'm not sure how Instant Run is working under the covers (my app does not use multidex).
Does anyone know how I can get a list of classes like this with Instant Run turned on? Or does anyone know exactly why Im seeing this behaviour (would be great to understand it)?
We can handle the DEX files built by instant run in application data path.
public class MultiDexHelper {
private static final String EXTRACTED_NAME_EXT = ".classes";
private static final String EXTRACTED_SUFFIX = ".zip";
private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator +
"secondary-dexes";
private static final String PREFS_FILE = "multidex.version";
private static final String KEY_DEX_NUMBER = "dex.number";
private static SharedPreferences getMultiDexPreferences(Context context) {
return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ?
Context.MODE_PRIVATE :
Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
}
/**
* get all the dex path
*
* #param context the application context
* #return all the dex path
* #throws PackageManager.NameNotFoundException
* #throws IOException
*/
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper",
"getSourcePaths sourceDir=" + applicationInfo.sourceDir + ", dataDir=" + applicationInfo.dataDir);
}
List<String> sourcePaths = new ArrayList<String>();
sourcePaths.add(applicationInfo.sourceDir); //add the default apk path
//the prefix of extracted file, ie: test.classes
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
//the total dex numbers
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "getSourcePaths totalDexNumber=" + totalDexNumber);
}
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//for each dex file, ie: test.classes2.zip, test.classes3.zip...
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
sourcePaths.add(extractedFile.getAbsolutePath());
//we ignore the verify zip part
} else {
throw new IOException("Missing extracted secondary dex file '" +
extractedFile.getPath() + "'");
}
}
try {
// handle dex files built by instant run
File instantRunFilePath = new File(applicationInfo.dataDir,
"files" + File.separator + "instant-run" + File.separator + "dex");
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "getSourcePaths instantRunFile exists=" + instantRunFilePath.exists() + ", isDirectory="
+ instantRunFilePath.isDirectory() + ", getAbsolutePath=" + instantRunFilePath.getAbsolutePath());
}
if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {
File[] sliceFiles = instantRunFilePath.listFiles();
for (File sliceFile : sliceFiles) {
if (null != sliceFile && sliceFile.exists() && sliceFile.isFile() && sliceFile.getName().endsWith(".dex")) {
sourcePaths.add(sliceFile.getAbsolutePath());
}
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "getSourcePaths parse instantRunFilePath exception", e);
}
return sourcePaths;
}
// /**
// * get all the classes name in "classes.dex", "classes2.dex", ....
// *
// * #param context the application context
// * #return all the classes name
// * #throws PackageManager.NameNotFoundException
// * #throws IOException
// */
// public static List<String> getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException {
// List<String> classNames = new ArrayList<String>();
// for (String path : getSourcePaths(context)) {
// try {
// DexFile dexfile = null;
// if (path.endsWith(EXTRACTED_SUFFIX)) {
// //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
// dexfile = DexFile.loadDex(path, path + ".tmp", 0);
// } else {
// dexfile = new DexFile(path);
// }
// Enumeration<String> dexEntries = dexfile.entries();
// while (dexEntries.hasMoreElements()) {
// classNames.add(dexEntries.nextElement());
// }
// } catch (IOException e) {
// throw new IOException("Error at loading dex file '" +
// path + "'");
// }
// }
// return classNames;
// }
/**
* scan parent class's sub classes
*
* #param context
* #param packageName
* #param parentClass
* #param <T>
* #return
*/
public static <T> Set<Class<? extends T>> scanClasses(Context context, String packageName, Class<T> parentClass) {
Set<Class<? extends T>> classes = new HashSet<Class<? extends T>>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (String path : getSourcePaths(context)) {
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "scanClasses path=" + path);
}
try {
DexFile dexfile = null;
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "scanClasses className=" + className);
}
if (className.toLowerCase().startsWith(packageName.toLowerCase())) {
Class clazz = classLoader.loadClass(className);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper",
"scanClasses clazz=" + clazz + ", parentClass=" + parentClass + ", equals=" + clazz
.getSuperclass().equals(parentClass));
}
if (clazz.getSuperclass().equals(parentClass)) {
classes.add(clazz);
}
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "scanClasses Error at loading dex file '" +
path + "'", e);
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "scanClasses exception", e);
}
return classes;
}
}
I am trying to get the string of the EditText and if it is " I want to get " also but the string is appearing this in the EditText
android.support.v7.widget.AppCompatEditText{42e33310 VFED..CL .F ....ID40,40-1160,315 #7f0c006f app:id\CalculatorDisplay}
after getting it and giving it to the EditText again
and here is a part of the onClick that works when I press a button and the rest of it is not dealing with this problem the problem is in NoRepeatNumber as I tested that when I get it in TestTV TextView I saw the code above
public void onClick(View view) {
TestTV.setText("enterd ");
String buttonPressed = ((Button) view).getText().toString();
PublicButtonPressed = buttonPressed;
if (DIGITS.contains(buttonPressed)) {
SinOrNumber=2;
TestTV.append("numbering");
// digit was pressed
if (!Started) {
mCalculatorDisplay.setText("");
TestTV.append("\nenterd setText");
} else {
TestTV.append("\nenterd setText eslse");
}
if (Started) {
OperationEnded = true;
}
if (NumberingEnded) {
FullNumber = String.valueOf(Number[Ni]);
Ni++;
NumberingEnded = false;
FullCalculation= FullNumber + " ";
NoRepeatNumber="";
DisplayCalculations();
}
FullNumber = FullNumber + buttonPressed;
Number[Ni] = Integer.parseInt(FullNumber);
TestTV.append("\nNumber[" + Ni + "] =" + + Number[Ni] + "\n" + "buttonPressed =" + buttonPressed + "\nFullNumber =" + FullNumber);
//DisplayCalculations();
if ("".equals(mCalculatorDisplay)) {
TestTV.append("\nentered equal\"\"");
NoRepeatNumber="";
} else {
if (NoRepeatNumber == "") {
NoRepeatNumber = String.valueOf(mCalculatorDisplay);
TestTV.append("\nNoRepeatNumber =" + NoRepeatNumber);
}
}
if(!NumberingEnded){
mCalculatorDisplay.setText(NoRepeatNumber + FullNumber);
}
if (!Started) {
Started = true;
}
if (userIsInTheMiddleOfTypingANumber) {
if (buttonPressed.equals(".") && mCalculatorDisplay.getText().toString().contains(".")) {
// ERROR PREVENTION
// Eliminate entering multiple decimals
} else {
Number[Ni] = Integer.parseInt(FullNumber);
}
}
userIsInTheMiddleOfTypingANumber = true;
} else {}}
Call mCalculatorDisplay.getText().toString() instead of String.valueOf(mCalculatorDisplay).
I guess mCalculatorDisplay is an EditText?
Update: NoRepeatNumber = String.valueOf(mCalculatorDisplay); - this is the problem part
i have an FTP connection which is working fine , i can download my files using a fragment which call > an asynctaskt which call > the FTP server
public static void downloadDirectory(FTPClient ftpClient, String parentDir,
String currentDir, String saveDir) throws IOException {
String dirToList = parentDir;
if (!currentDir.equals("")) {
dirToList += "/" + currentDir;
}
FTPFile[] subFiles = ftpClient.listFiles(dirToList);
if (subFiles != null && subFiles.length > 0) {
for (FTPFile aFile : subFiles) {
String currentFileName = aFile.getName();
if (currentFileName.equals(".") || currentFileName.equals("..")) {
// skip parent directory and the directory itself
continue;
}
String filePath = parentDir + "/" + currentDir + "/"
+ currentFileName;
if (currentDir.equals("")) {
filePath = parentDir + "/" + currentFileName;
}
String newDirPath = saveDir + parentDir + File.separator
+ currentDir + File.separator + currentFileName;
if (currentDir.equals("")) {
newDirPath = saveDir + parentDir + File.separator
+ currentFileName;
}
if (aFile.isDirectory()) {
// create the directory in saveDir
File newDir = new File(newDirPath);
boolean created = newDir.mkdirs();
if (created) {
System.out.println("CREATED the directory: "
+ newDirPath);
} else {
System.out.println("COULD NOT create the directory: "
+ newDirPath);
}
// download the sub directory
downloadDirectory(ftpClient, dirToList, currentFileName,
saveDir);
} else {
// download the file
boolean success = downloadSingleFile(ftpClient, filePath,
newDirPath);
if (success) {
System.out.println("DOWNLOADED the file: " + filePath);
} else {
System.out.println("COULD NOT download the file: "
+ filePath);
}
}
}
try {
Log.v("LogoutInformation", "Logout from FTP");
ftpClient.logout();
} catch (Exception e) {
Log.e("LogoutInformation", "Logout Fail");
}
try {
Log.v("DisconnectInformation", "Disconnect from FTP");
ftpClient.disconnect();
} catch (Exception e) {
Log.e("DisconnectInformation", "Disconnect Fail");
}
}
Here my function from FTPserver.
System.out.println("DOWNLOADED the file: " + filePath);
And this is what I want to show on my fragment , i want to make a toast which display the file which is actually in download.
But it's background processing , so i can't display information on my fragment , so i don't know how i can do it.
As from tags, you are using AsyncTask. Modify it's constructor to accept a reference to context.
public class MyAsyncTask extends AsyncTask<..> {
private Context mContext;
public MyAsyncTask(Context c){
this.mContext = c;
}
...
When your task is running in doInBackground, you should publishProgress(String):
doInBackground(String.. params){
for(something){
...
publishProgress(fileName);
}
}
publishProgress(String fName){
Toast.makeText(mContext, name, Toast.Length_long).show();
}
You only need to do this.
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(context,"Your text", Toast.LENGTH_LONG).show();
}
});
Are you calling the downloadDirectory in the doInBackground event of the AsyncTask class?
If so, you can make Toast message like so:
#Override
protected void onPostExecute(Void result) {
Toast.makeText(getActivity(),"DOWNLOADED the file: "+filePath,Toast.LENGTH_SHORT).show();
}
Where you make filePath global variable.
Something like that. Hope it helps..
Try to use:
Toast.makeText(getActivity(), "File Downloaded:"+currentFileName, Toast.LENGTH_LONG).show()