Saturday, December 7, 2013

requestFeature() must be called before adding content

Lets start with the reason for this error:

YOU MUST NOT REQUEST A WINDOW FEATURE, AFTER ADDING CONTENT TO UI ENTITY.

By UI Entity I mean, Activity or dialog, etc.

I had a unique desire... to rebuild my Activity and its content upon language switching from a menu I've build here, without the Activity re-creation which most times causes flickering

Well that was not a complicated task, but quite surprising that I had to use this:

private static Field windowContentParent;

static {
    try {
        windowContentParent = Class.forName("com.android.internal.policy.impl.PhoneWindow").getDeclaredField("mContentParent");
        windowContentParent.setAccessible(true);
    } catch (Exception e) {
        Log.e("REFLECTION STATIC", "Cannot extract PhoneWindow.mContentParent Field");
    }
}


And then when you want to remove the content: 


View rootView = screen.getRootView();
((ViewGroup) rootView.getParent()).removeAllViews();
windowContentParent.set(getWindow(), null);

 



Why does this work?

The only thing indicating that the content had been set is the parent instance of the window, once you turn it to null, it is as if your activity had just been created.

Although this works for me, and on every android version I've tested(2.1 - 4.4), this is very hackey... Use this with caution!!!


---- UPDATE ----

Well, I should have wrote: USE THIS WITH CAUTION!!!!
I've just spent a couple of hour debugging an issue where an entity within the activity registers as a listener, and since the life cycle of the activity was not called, bad things happened, from weird UI behavior, to memory leaks...

So be careful! and be smart... I've ended up performing the terminating life cycle of the activity by calling pause, stop, destroy, logic, without calling onPause, onStop, onDestroy.


Monday, August 19, 2013

Android - WebView - JavaScript - onPageFinish() and stuff in between

OK... so let start by saying I'm working with some very talented people...

I've been digging in Android sources for days searching for the cause for this, I've even offered a bounty for 100 points which no one claimed.

I've seen many similar issues such as this, across StackOverFlow...

The main issue is that the loading of the page is completed, and then the onPageFinished event is called with a seemly random delay that can range from 0.1 - 40 sec, only after the <GATE-M>DEV_ACTION_COMPLETED</GATE-M> is printed to the log.

Here is the code snippet:

webView = (WebView) view.findViewById(R.id.WebView);

webView.setWebViewClient(new WebViewClient() {

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        logDebug("Loading URL: " + url);
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return WrappingClass.this.shouldOverrideUrlLoading(view, url);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        logInfo("Injecting JavaScript to webview.");
        webView.loadUrl("full-js-here");
    }

    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        logError("error code:" + errorCode);
        super.onReceivedError(view, errorCode, description, failingUrl);
    }
});

WebSettings webSettings = webView.getSettings();
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
webSettings.setJavaScriptEnabled(true);
webView.requestFocus(View.FOCUS_DOWN);
webView.loadUrl("url");

So the solution is quite a hack but it is wonderful... Check it out:

Somewhere in your class declare the following:
final class ObjectExtension {

    @JavascriptInterface
public void onLoad() { logInfo("onLoadCompleted"); WrappingClass.this.onLoadCompleted(); } }
public void onLoadCompleted() {
    webView.loadUrl("full-js-here");
}


And before the URL loading add the following:
webView.addJavascriptInterface(new ObjectExtension(), "webviewScriptAPI");
String fulljs = "javascript:(\n    function() { \n";
fulljs += "        window.onload = function() {\n";
fulljs += "            webviewScriptAPI.onLoad();\n";
fulljs += "        };\n";
fulljs += "    })()\n";
webView.loadUrl(fulljs);
webView.loadUrl("url");

This registers a callback for the onLoad event of the WebView window, which is loaded long time before the onPageFinished is called, because of that Android WebView issue.

So the trick is that we inject the onLoad callback before loading the url, later (next line) we load the url, once the onLoad callback is called, in our onLoad implementation we call to the Java API, which in its turn inject the Javascript into the loaded page, and sometime long after that the onPageFinished is called.

End of story...
---- UPDATE ----

It has been a very long while since that post... I've wrote SocialApp(link at the top Left), which is entirely a WebView application, a multi WebView applications, which runs Javascripts on all of the WebViews and monitor the beginning and ends of scripts, and runs Javascript on a WebViews in the background....

What I'm trying to say is, if you have any questions, I'm pretty sure I can answer them, so ask away...


---- UPDATE ----

It has been a very long while since I posted that update, I really hoped to release it as an open source, and was struggling with it for a long time, but as it was pretty much forced on me due to the architecture I've been decided to append this "CyborgWebView" to Cyborg, which is a license based SDK, You can find it here.

Also, if the post is not clear enough, and you prefer a sample project, let me know...







Monday, August 12, 2013

Multi-Language Android application

I'm going to add localization to our application...

I know Android to its core, but I haven't used localization before, (never needed to).

So I'm so thrilled to start this new journey, and be able to share my insights...

Given the fact that I'm a critic, this should be joyful :)

So here I go:

If I've got the basic correct, then:

  • If you want to target a specific locale, your values folder containing the strings.xml should be 'values-xx-rYY', note that the locale region referred in the specs is with underscore, while the values folder MUST NOT have underscores in them.
  • If you want to target a group of locales, and by group I mean locales which fits 'xx_*', then your values folder should be 'values-xx'.
First you would like to know which are the available languages, I've got it from here:
(I have no idea how credible the data is, but it is a start)

Arabic, Egypt (ar_EG)           
Arabic, Israel (ar_IL)          
Bulgarian, Bulgaria (bg_BG)     
Catalan, Spain (ca_ES)          
Czech, Czech Republic (cs_CZ)   
Danish, Denmark(da_DK)          
German, Austria (de_AT)         
German, Switzerland (de_CH)     
German, Germany (de_DE)         
German, Liechtenstein (de_LI)   
Greek, Greece (el_GR)           
English, Australia (en_AU)      
English, Canada (en_CA)         
English, Britain (en_GB)        
English, Ireland (en_IE)        
English, India (en_IN)          
English, New Zealand (en_NZ)    
English, Singapore(en_SG)       
English, US (en_US)             
English, South Africa (en_ZA)   
Spanish (es_ES)                 
Spanish, US (es_US)             
Finnish, Finland (fi_FI)        
French, Belgium (fr_BE)         
French, Canada (fr_CA)          
French, Switzerland (fr_CH)     
French, France (fr_FR)          
Hebrew, Israel (he_IL)          
Hindi, India (hi_IN)            
Croatian, Croatia (hr_HR)       
Hungarian, Hungary (hu_HU)      
Indonesian, Indonesia (id_ID)   
Italian, Switzerland (it_CH)    
Italian, Italy (it_IT)          
Japanese (ja_JP)                
Korean (ko_KR)                  
Lithuanian, Lithuania (lt_LT)   
Latvian, Latvia (lv_LV)         
Norwegian-Bokmol, Norway(nb_NO) 
Dutch, Belgium (nl_BE)          
Dutch, Netherlands (nl_NL)      
Polish (pl_PL)                  
Portuguese, Brazil (pt_BR)      
Portuguese, Portugal (pt_PT)    
Romanian, Romania (ro_RO)       
Russian (ru_RU)                 
Slovak, Slovakia (sk_SK)        
Slovenian, Slovenia (sl_SI)     
Serbian (sr_RS)                 
Swedish, Sweden (sv_SE)         
Thai, Thailand (th_TH)          
Tagalog, Philippines (tl_PH)    
Turkish, Turkey (tr_TR)         
Ukrainian, Ukraine (uk_UA)      
Vietnamese, Vietnam (vi_VN)     
Chinese, PRC (zh_CN)            
Chinese, Taiwan (zh_rTW)

Next thing you would like to do, is be able to switch the languages dynamically.

Why? Simply because it would take far less time to evaluate each screen while switching languages, and been able to see the twigging each causes.
To do that you will first need to distinct whether the application runs in debug mode, or in production...

What I've done, is added a menu while running in debug, then launched a dialog for choosing the language I want to display.

A code snippet for changing the Locale:

 Resources res = getApplicationContext().getResources();
 DisplayMetrics dm = res.getDisplayMetrics();
 android.content.res.Configuration conf = res.getConfiguration();
 conf.locale = newLocale;
 res.updateConfiguration(conf, dm);

Afterwards I've tried to dismiss the dialog which for some weird reason it did not work... but I've found the solution for it.

The last part is rendering the UI... That was one of the biggest hacks I faced!
I'll start of and say that in order to render the UI without writing 10 TONs of code, you should(I think MUST) have a parenting layer of, Application, BaseActivity, BaseFragment, BaseDialogFragment, and so on...

Since the only way (I could find) to check if the locale had change within an activity, in its onResume you need to compare the Context.getResources().getConfiguration().locale, with a value which you save on each onResume, in your BaseActivity, this value I believe can be static.

Once you've recognize a change in the locale, there are a couple of approaches you can and need to take:
  • You can refresh your activity with its intent. (Re-launch the activity and close the current one)
  • You can refresh your activity manually. (Rener the views one by one)
  • If you are using a ViewPager, make sure you are setting its adapter via Handler.post(...), otherwise you might get a 'Recursive entry to executePendingTransactions' error.
  • Since I'm loading all my fragment dynamically, after an activity has gone to background, e.g. the onSaveInstanceState was called on the fragments, I could not commit the new changes to the FragmentTransaction, I had to use the commitAllowingStateLoss.
And I was done...

All took less then a day, there is no greater satisfaction then to see the languages changes live in front of your eyes!

Good luck...

(I'm going to number these as I think there are going to be more then one... prof is in the comments :))
NOTES:

  1. Since Android 4.2 the API to change the Device's Locale has been disabled for non-os-signature applications, so all the Locale changing apps are DOOMED...

-- UPDATE --

I've released Cyborg not too long ago, and Language Swapping is build in and optimized feature in, You can find it here.

Wednesday, August 7, 2013

Custom view will not render

If you have ever created a custom view, one which inherit from ViewGoup, and by chance had no Children... actual View children, and then tried to override its onDraw(...), or draw(...), and you have called invalidate in vain, debugged for far too long only to find out that view will not render...

Well the reason actually makes sense, it is a container... there are no children SO... DO NOT RENDER!

But still, it might have a background... ?

In any case to solve this, you are required to add the following line to all of your custom view constructors:

super(...);
setWillNotDraw(false);

This would force the view group to render... although it has no children.

Monday, August 5, 2013

Android - APK Signature check programmatically in runtime

I've been at this for some time... I've tried to make my application distinct between debug version, release version, in some cases staging version, free, paid premium... and the list goes on and on...

So... I've asked the pro's, it was hard for me to describe the question, but I think it is right. I got no answer so I've went and searched a bit, and found an article explaining how this things work, I'm not going to go by all the explanation, I'll just lay it down.

First we define the certificates:

private enum SignedCertificate {
 Debug("...", null, true, true),
 Staging("...", Environments.Staging, false, true),
 Production("...", Environments.Production, false, false),
 Unknown("There is no such a certificate", Environments.Production, false, false), ;

 private final String md5;

 private final Environments environment;

 private final boolean debugMode;

 private final boolean showsLogs;

 private SignedCertificate(String md5, Environments environment, boolean debugMode, boolean showsLogs) {
  this.md5 = md5;
  this.environment = environment;
  this.debugMode = debugMode;
  this.showsLogs = showsLogs;
 }

 public static final SignedCertificate getCertificate(String md5) {
  md5 = md5.toLowerCase();
  for (SignedCertificate certificate : values()) {
   if (certificate.md5.toLowerCase().equals(md5))
    return certificate;
  }
  return Unknown;
 
}

The MD5 for each certificate can be copied from the export dialog in the final step of exporting a signed application in Eclipse, for debug md5 sign it with you debug keystore.

In order to retrieve the current certificate in runtime:

private void checkCertificate() {
 try {
  PackageManager pm = application.getPackageManager();
  Signature sig = pm.getPackageInfo(application.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
  String md5Fingerprint = doFingerprint(sig.toByteArray(), "MD5");
  certificate = SignedCertificate.getCertificate(md5Fingerprint);
 } catch (Exception e) {
  logInfo("Error getting certificate, assuming release version...", e);
  certificate = SignedCertificate.Production;
 } finally {
  logInfo("Found Certificate: " + certificate.name());
  Log.setShowLogs(certificate.showsLogs);
 }
}

protected static String doFingerprint(byte[] certificateBytes, String algorithm)
  throws Exception {
 MessageDigest md = MessageDigest.getInstance(algorithm);
 md.update(certificateBytes);
 byte[] digest = md.digest();
 
 String toRet = "";
 for (int i = 0; i < digest.length; i++) {
  if (i != 0)
   toRet += ":";
  int b = digest[i] & 0xff;
  String hex = Integer.toHexString(b);
  if (hex.length() == 1)
   toRet += "0";
  toRet += hex;
 }
 return toRet;
}

After retrieving the certificate, implement any logic you may want according to the certificate, I've used the enum to define logs or debug mode, working environment and so on...

The nice thing about this implementation, is that it does not care about the application package name Google uses... if you have shared code between free and paid version of the application, and you would like to distinct them programmatically, I think this is a very nice solution...

Leave your comments below... :)

-- UPDATE --

I've released Cyborg not too long ago, and this Certificate Validation is build in and optimized further in terms of API and how much code you need to write to get it to work, You can find it here.

Dynamic DialogFragment - Android

This is frustrating... I've been playing with this stupid DialogFragment for half a day, trying all sort of pieces of code, Questions from StackOverFlow, just to realize, that they are all useless!!

It is as simple as the fact that when using the DialogFragment the LayoutParams of the parent view are not been set, thus renders the dialog badly.

After some thinking about it I've came up with this:

In res/values/stylex.xml:

<resources>
    ...
    <style name="AlertDialogStyle" >
        <item name="android:cacheColorHint">@android:color/transparent</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>
   ...
</resources>

In res/layout/alert_dialog_body.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/tools"
    android:id="@+id/xx"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@android:drawable/alert_dark_frame"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="10dp"
            android:text="Large Text"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="2dp"
            android:layout_marginRight="2dp"
            android:layout_marginTop="5dp"
            android:contentDescription="Separator"
            android:scaleType="fitXY"
            android:src="@drawable/settings_separator"
            app:ignore="HardcodedText" />

        <TextView
            android:id="@+id/AlertBody"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="5dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="5dp"
            android:text="TextView"
            android:textAppearance="?android:attr/textAppearanceMedium" />

        <Button
            android:id="@+id/DialogButton"
            style="?android:attr/buttonStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="15dp"
            android:text="Button" />
    </LinearLayout>

</RelativeLayout>
(The RelativeLayout is the trick...)

Separator Image
In res/drawable/... filename.png





and the Dialog fragment:

public class MyDialogFragment
  extends DialogFragment {
 
 protected final String TAG = getClass().getSimpleName();
 
 private int titleId;
 
 private int bodyId;
 
 private int buttonId;
 
 public MyDialogFragment(int titleId, int bodyId, int buttonId) {
  super();
  this.titleId = titleId;
  this.bodyId = bodyId;
  this.buttonId = buttonId;
 }
 
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  
  View view = inflater.inflate(R.layout.alert_dialog_body, container);
  TextView title = (TextView) view.findViewById(R.id.Title);
  TextView body = (TextView) view.findViewById(R.id.AlertBody);
  Button button = (Button) view.findViewById(R.id.DialogButton);
  
  button.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    getDialog().dismiss();
   }
  });

  button.setText(buttonId);
  title.setText(titleId);
  body.setText(bodyId);
  return view;
 }
 
 @Override
 public void onAttach(android.app.Activity activity) {
  super.onAttach(activity);
 }
 
 @Override
 public void onCreate(android.os.Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AlertDialogStyle);
 }
 
}

And Just in case you would actually want to use this:

MyDialogFragment dialogFragment = new MyDialogFragment(R.string.title,R.string.body,R.string.button_label);
dialogFragment .setCancelable(false);
dialogFragment .show(getSupportFragmentManager(), "Dialog Fragment");

You can check out the code here.

Leave your comments below... :)










Sunday, May 12, 2013

Android Google's Calendar API

That is really sad, I've taken a look at the Google calendar API code, and I just can't believe it... these guys should really read my posts... or at least ask for some advice... of someone else who knows WTH he is doing!!!

I know it is a beta and all, but it is such a horrible beta... it is unbelievable, the hierarchy does not make any sense, when he needs to use inheritance they don't, when they uses it, it is an abuse of the hierarchy mechanism, the use of generics is absolutely wrong, and the use of virtual methods with or without generics is absolutely ridiculous, the code duplicate seem to never end, and the separation to projects seems to be correct but it is not!

I've investigated the API to its core... all the jars attached to the calendar-android-sample, I've attached all the sources to one project, and saw the reflective workaround used to load the classes, there was nothing sophisticated about it.

I've tried to understand how the API works to write a simpler implementation(I'm very good at that!), it is impossible... Google are right to publish their API as open source, why the hell to obfuscate the code if you don't need to ha...? why not just to make a darn mess and no one would be able to understand WTH it does...?

I've tried to avoid writing this, but it burns and upsets me and makes me sad a pile of code written this way.

So I've tried to change my approach... since I want the Calendar API, and I wanted to use it I've tried just to scratch the surface of the sample project... that has made me sad...

I've found the following row:


CalendarList feed = client.calendarList().list().setFields(CalendarInfo.FEED_FIELDS).execute();

can anyone guess WTH it does?

let me tell you, it is a Calendar instance returning a new instance of a CalendarList, which returns a List (not the one you think) of initialized results (which results???), for which you set a field values for the response, for which you have virtual method to cast the return value back to a List, and then calls... execute()...???

execute()????
execute()????
execute()????
execute()????

execute what on a darn list... can you explain... 

ha... yes it inherits from a CalendarRequest with a generic parameter T, oh yes and T is a... wait T can be four different types, now I confused... there are:
  • 1 Clear
  • 1 Instances
  • 1 Query
  • 1 QueryAdd
  • 1 Move
  • 4 Inserts
  • 4 Updates
  • 4 Patches
  • 4 Deletes
  • 6 Gets
Don't you think there might be a better implementation than to override all the darn methods, and cast the return type!

I've always thought Google has the best minds working for them, I've also been to a lecture about the brilliant continues integration system they have build, and trust me it is brilliant, I've meet the guy who designed it, he is very keen, but this just goes to show me, that I my gut was right about not continuing with the attempt to work for Google, I would have for sure ended up resigning, because if this is the infrastructure they release, and I guess they are proud of their "success" (which 'sucks S') then they are lucky to have a lot of money, because it would cost them very much to keep it up!

I know the cost of bad code... I've tried to avoid it for long enough, everywhere I've worked, and seen what bad code can do to huge teams of development... the bottom line... it costs MONEY...

Luckily Google has enough of it :)

Wednesday, April 10, 2013

Android C2DM to GCM

I'm trying... really trying to be nice, to give these guys a chance, but boy, they have done it this time...

Long ago, I've implemented a C2DM Module for Cyborg in less than a day, which was elegant tight, and generic... and a few(3) weeks later, Google has announced that C2DM is dying, and now there is GCM... terrific...

Since I'm short on time, I've implemented a GCM Module for Cyborg, happily using the gcm.jar, and its updates every now and again.

Today, I've actually looked at the code of the gcm.jar, and I was shocked, shocked to find out that this code violates nearly all of my basic coding conventions, this is a sort of code I would have written when I've just started to work with Java, and have tried then to challenge the language conventions.

I've been dealing with very bad code for the last years, but this... I cannot make heads or tails of it...
It is noodles all over the place, using too many static methods and members calling back and forth mixed with broadcast receivers and services all together throwing a swingers party... it is bad Java, mixed with bad Android, if Java could have spoken, it would have shouted "RAPE !!!", it is shamley unbelievable.

This is crazy, I know that "Anyone can cook", but would you actually eat anyone's cooking?
I'm expecting more from you Google!

I'm now in the middle of getting rid of this darn jar file, and I see that the basic registration to the GSF service remained practically the same, they have simply mapped some errors and some use cases, with nasty(ing) if statements.

...
After 2 days...
...

I've finally managed sending a GCM from a device to itself via my server, I've used my JSONer to serialize the and deserialize the JSON, and was surprised to discover that the actual GCM payload MUST be JSON Array...

WTF?

If you already force something, force it to be a JSON Object, and let the people use whatever stupid implementation they want within that object.

Force the payload to be an JSON Array, and to add a key - value extras in the intent which derived from the array... it is an unnecessary overhead!!

Any logical application would use a JSON Object as the payload, and use GSON or any other parser to serialize and deserialize the payload.

Friday, April 5, 2013

Check if an application is installed on the device

This is quite embarrassing... I've used to use the following to perform that check:

private static final String GooglePlayStorePackageName = "com.google.market";

void someMethod() {
    packageManager = getApplication().getPackageManager();
    List<PackageInfo> packages = packageManager.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
    for (PackageInfo packageInfo : packages) {
        if (packageInfo.packageName.equals(GooglePlayStorePackageName)) {
            googlePlayStoreInstalled = true;
            break;
        }
    }

Since I cannot stand my own stupidity, and I love to lecture people about how amazing exceptions are and how stupid it is to ignore them, I found that I have done it as well, although not in the same context I lecture people...

Following is the proper code to check whether an application is installed on your device:

protected final boolean isPackageInstalled(String packageName) {
    try {
        application.getPackageManager().getPackageInfo(packageName, 0);
    } catch (NameNotFoundException e) {
        return false;
    }
    return true;
}
As you can see, I've ignored the fact there is a method which throws an exception when it could not find the requested package, which is so dumb of me.

So now I wrapped the method with a boolean returning method, and do use the exception to determine whether the application package is installed or not.