[Q] Changing smali using apktool gives problem with r.Java

Search This thread

john37

Member
Jul 7, 2010
7
0
I've been stuck for a while now, trying to add some new functionality (day counter) to an existing apk (widget).

What I've done so far:
- unpacked the apk using apktool (using apktool d -d)
- start new project in netbeans with the specific source
- adding the java class to the existing package

The java code introduces some new parameters. I've added the parameters to different xml's. When I try to rebuild I keep getting the message:

cannot find symbol
symbol : variable layout
location: class fr.gdi.android.news.R
remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);

Can anyone give me a hint what to do?
 

john37

Member
Jul 7, 2010
7
0
some additional info. This is the class I'm trying to add:

Code:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.gdi.android.news;

/**
 *
 * @author John
 */
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.widget.RemoteViews;

public class CountDays extends AppWidgetProvider {
	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
	                                                           int[] appWidgetIds) {
	       Timer timer = new Timer();
	       timer.scheduleAtFixedRate(new MyTime(context, appWidgetManager), 1, 60000);
	  }
	  private class MyTime extends TimerTask {
	         RemoteViews remoteViews;
	         AppWidgetManager appWidgetManager;
	         ComponentName thisWidget;
	         public MyTime(Context context, AppWidgetManager appWidgetManager) {
	         this.appWidgetManager = appWidgetManager;
	         remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
	         thisWidget = new ComponentName(context, CountDays.class);
	         }
	         @Override
	         public void run() {
	        	Date date1 = new Date();
	     		Calendar calendar = new GregorianCalendar(2010, 11,25);
	     		long days = (((calendar.getTimeInMillis()- date1.getTime())/1000))/86400;
	                 remoteViews.setTextViewText(R.id.days,""+ days);
	                 appWidgetManager.updateAppWidget(thisWidget, remoteViews);
	         }
	  }
	}
 
Last edited:

Brut.all

Inactive Recognized Developer
Jul 27, 2009
1,471
352
1. Apktool does not support writing your code in Java right now. Whole story is here: http://code.google.com/p/android-apktool/issues/detail?id=88 . If you want to use Java, it's possible, but you will have to use a workaround. It's not that hard to implement, but requires you to understand, how Java/javac/bytecode works. Workaround is described in above link, if you don't want to read full story, instructions start at "My script is here".

2. I think (but maybe I'm wrong) that you misunderstand, how R references work. These R.drawables.xxx strings aren't magically converted to resIDs at compile or run time. To use them you have to generate proper R class using SDK. If you use Eclipse or Netbeans, you don't have to do that, cause IDE regenerates R.java file each time you modify something in resources, so you can use R references in your code whenever you want.

In app decoded by apktool you have to manage resIDs manually. It's caused by the fact, that if you want to use some resources in smali, you can't let SDK generate ids for these resources automatically, cause resIDs are stored in smali in numerical form, i.e. "const v0, 0x7f012345", not something like "const v0, R.drawables.xxx". So if you would add e.g. "const v0, 0x7f012345" line to smali and resIDs would be regenerated, your line might reference other resource, that you want.

Of course that's not the case if you want to use resources from Java code, but as I said, this feature isn't fully supported by apktool. Also I think it would be good to add possibility to generate resIDs automatically, even for smali code, cause managing resIDs manually is very inconvenient. But that's future.

For now if you want to add new resource and use it from your Java/smali code, you have to modify public.xml file, add that resource with next free resID and then use that resID from your code.

If you want to know more, you could read this: http://forum.xda-developers.com/showthread.php?p=7380557#post7380557
 
Last edited:

john37

Member
Jul 7, 2010
7
0
@Brut.all: I think you're right about me not understanding R.java completely. But what I understand from your story is this. When I want a new resource to use in a new class and to be used in the existing xml, I would have to do the following:

- add a line to the res/values/ids.xml, for example:
Code:
<item type="id" name="main">false</item>
- add a line to the res/values/public.xml, for example:
Code:
<public type="id" name="main" id="0x7f0a0034" />
- add a line to the R$id.java and R$layout.java (don't exactly know which one)
Code:
.field public static final main:I = 0x7f0a0034

first: is this correct? second: How can I refer to the newly added resource in my new class. R.layout.main doesn't work, R$layout.main doesn't work.
Code:
remoteViews = new RemoteViews(context.getPackageName(), R$layout.main);
 

Brut.all

Inactive Recognized Developer
Jul 27, 2009
1,471
352
- add a line to the res/values/ids.xml, for example:
Code:
<item type="id" name="main">false</item>

You don't have to do that. If you add new drawable, XML or @+id/something value, then it's ok.

- add a line to the res/values/public.xml, for example:
Code:
<public type="id" name="main" id="0x7f0a0034" />

Yes.

- add a line to the R$id.java and R$layout.java (don't exactly know which one)
Code:
.field public static final main:I = 0x7f0a0034

No ;-)

If you want to use these resources in smali, then you could use them as is, e.g.: "const v0, 0x7f0a0034".

If you want to use my workaround to work in Java, then you could do the same, as above, e.g.:

Code:
int id = 0x7f0a0034;

or you could do this cleaner and generate R class manually: remove all existing R*.java classes (they aren't used) and create single R.java file:

Code:
public final class R {
    public static final class id {
        public static final int main = 0x7f0a0034;
    }
}

and then you could use it as "R.id.main" in your code.

But first you have to implement workaround to use any Java code.
 

john37

Member
Jul 7, 2010
7
0
@Brut.all: thanks again for your reply. I think I didn't understand your previous story fully. I am 'developing' on windows, so your workaround is going to be tricky. Do you know if anyone has ported your script to windows?
 

john37

Member
Jul 7, 2010
7
0
Some additional info: I don't want to use a new resource in the existing smali code. What I want is a new resource, say @id/main, to be used in the xml files and in a java class, which describes the method that should be invoked when the widget updates.

What I've done now is that I've made a new R$main class with the resources I want to use. I also made the java class with the method and it refers to the resources mentioned in the R$main class. This way I can build it without problems in Netbeans. But when I want to use apktool, I keep getting the message:

Code:
I: Checking whether sources has changed...
I: Smaling...
Exception in thread "main" java.lang.NullPointerException
        at org.jf.util.PathUtil.getRelativeFile(Unknown Source)
        at org.jf.smali.smaliFlexLexer.getSourceName(Unknown Source)
        at org.jf.smali.smaliFlexLexer.getErrorHeader(Unknown Source)
        at org.jf.smali.smaliFlexLexer.nextToken(Unknown Source)
        at org.antlr.runtime.CommonTokenStream.fillBuffer(Unknown Source)
        at org.antlr.runtime.CommonTokenStream.LT(Unknown Source)
        at org.jf.smali.smaliParser.smali_file(Unknown Source)
        at brut.androlib.mod.SmaliMod.assembleSmaliFile(Unknown Source)
        at brut.androlib.src.DexFileBuilder.addSmaliFile(Unknown Source)
        at brut.androlib.src.SmaliBuilder.buildFile(Unknown Source)
        at brut.androlib.src.SmaliBuilder.build(Unknown Source)
        at brut.androlib.src.SmaliBuilder.build(Unknown Source)
        at brut.androlib.Androlib.buildSourcesSmali(Unknown Source)
        at brut.androlib.Androlib.buildSources(Unknown Source)
        at brut.androlib.Androlib.build(Unknown Source)
        at brut.androlib.Androlib.build(Unknown Source)
        at brut.apktool.Main.cmdBuild(Unknown Source)
        at brut.apktool.Main.main(Unknown Source)

Maybe I'm completely on the wrong track...
 

Brut.all

Inactive Recognized Developer
Jul 27, 2009
1,471
352
Sometimes I get NullPointerException instead of real smali errors, when I'm doing "apktool b". I think it's a bug in the apktool. Try to smali "smali" dir normally, without apktool to see errors.

But I don't really see much sense in adding any Java classes, because they just can't work without additional steps. What my script does, is:

  • javac all files from brut.iface
  • javac all files from brut.src against compiled brut.iface
  • convert compiled brut.src to smali. This is done in two steps:
    • dx them to classes.dex
    • baksmali them using baksmali or apktool

Most of commands from my script (javac, dx, apktool/baksmali) should work on a Windows. File operations, i.e. mkdir, rm, cp should be easily portable. I don't know, how to replace "find" command.

You could try to use Cygwin - then you should be able to run my script on a Windows.