Monday, November 21, 2016

Android send broadcast from adb command line with extras to a specific Receiver

Android documentation is super ridicules...

Here is what I could compose of multiple stackoverflow answers.

adb shell am broadcast -n ${packageName}/${BroadcastClass FQN} --es ${key} "%{value}"

Sunday, June 21, 2015

Extract DB From Your Android App - With Little Agony

So we all know how this goes right... some devices allow you access to your debuggable application's db files via a file browser and some don't. There is now right or wrong way for doing this so my solution will work on ALL devices though requires a little setup.

I really wonder how other people do it but here is the most aggravating way of doing it:

  1. Add a line in the code to copy the db file to the SDCard.
  2. Run the application and make it copy the file.
  3. Browse the SDCard and transfer the file to your computer.
  4. Open the file on your computer.

This seem like "Come on these are only few steps I'll manage" --- Sorry my time is valuable and this crappy way is not an acceptable solution.

So I've wasted 10 minutes on a setup that allows me to open my app's db file in less than 10 seconds...

I've installed Node.js and created this little server script which is running on my local machine:

var http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
 response.writeHead(200);
 var destinationFile = fs.createWriteStream("mydbfile.db");
 request.pipe(destinationFile);

 var fileSize = request.headers['content-length'];
 var uploadedBytes = 0;

 request.on('data', function(d) {
  uploadedBytes += d.length;
  var p = (uploadedBytes / fileSize) * 100;
  console.log("Uploading " + parseInt(p) + "%");
 });

 request.on('end', function() {
  response.end("File Upload Complete");
 });

}).listen(11225, function() {
 console.log("server started");
});


In my app code I have a button(it is actually an entire debug menu) that only exists in a debug version, otherwise visibility is set to GONE, and when I click on that button:

case FETCH_DB:
 HttpRequest request = new HttpRequest();
 request.setUrl(Environments.MyLocalServer.getBaseUrl() + "/upload");
 try {
  byte[] dbFile = Tools.readFullyAsByteArray(new FileInputStream(getDatabasePath("mydbfile.db")));
  request.setBodyAsByteArray(dbFile);
  getManager(HttpManager.class).executeRequestAsync(request);
 } catch (IOException e) {
  e.printStackTrace();
 }
 break;

If this code is not clear, it takes the file turns it to a byte array(yes I don't care about efficiency here its a debug function!!) adds it to a HttpRequest and sends it to my local server, where is lands in a designated folder and then in two clicks I can view my db!


THE END!

Monday, August 25, 2014

Custom android.app.Application and ClassNotFoundException

I've encountered this error so many times... and every single time it was something so stupid I had to waste (in total I would estimate) hours to solve, and sometimes stupidity repeats itself.

So I'll name here a few cases I remember:
  • When you encounter this in your published application, if you haven't done something extremely stupid, like not exporting the application class, then this is mostly an OS update thing... that it tries to launch the application between the removal of the older version and the installation of the newer one.
  • While you see this in your logcat when launching the application from your IDE, it can be one of the following:
    • You do not export the workspace dependencies of your Android application.
    • One or more of your Android libraries paths are incorrect.

Also sometimes you might have an error on your project node in the "package explorer", but all the content of the project seems fine, well this is because one of your Android libraries has one of the above dependency issue.

Hope I saved you a minute... :)

+1 if this helped you!

Monday, June 30, 2014

Upgrading to ADT 23 - Multiple issues

Google I/O 2014 had passed, and with it a breeze of new technologies came along, and of course we developers want to try out what coming.... so we take our IDE that took us hours to config, we take our ADT and Android-SDK that took us hours to download, and upgrade it all to work with the latest version......
...
...
And then it begins....

  • Eclipse will not install the new ADT plugin due to some conflicting dependencies.
  • Files are missing because their names or paths have changed.
  • Features are not working.
  • annotations.jar is missing.
  • zipalign is not there.
  • proguard is gone.
  • ...
  • ..
  • .
  • And only god knows what else I've missed (Or a Google Search)

Thing is about Google they know their shit, and they will not(in most cases) just release something half working, but it would be nice of them to let us(fellow developers) know that we need to reconstruct our work environment from scratch... I mean they should literally state somewhere "TO USE THE NEW ADT YOU MUST... blah blah" so we wont waste days on hoping for a fix that will not come.

So the solution is:

Download the new bundle Eclipse or Android Studio, follow the steps and setup your work environment e.g. your Android SDK and your IDE... then magically everything works.

OK, I've also worked out Proguard:
Go here and copy the content, create a file at: ${SDK}/tools/proguard/proguard-android.txt
(I'm not sure if that is the original content if not post a link to it)

Now go and download Proguard... this is an old bug they didn't bother to fix yet, I had to do this also last time I've installed the SDK, both at home and at work.

Extract the zip onto ${SDK}/tools/proguard/

And you are done, I think this will work on all environments, if not leave me a comment.

If this solves your issue  +1 it so others may find this as well, if not write a comment and lets see how to fix this.

Wednesday, April 30, 2014

Check if all your texts are in the Strings XML

So most people are going to say... whaaat? lint does this for me already...

Well, I hate lint(most of the time!) its a cool tool with tons of potential and zero usage for me!

It actually stand in my way of building testing and releasing my application most of the time, and the warnings are sometimes annoyingly annoying :)

I know you can configure this bugger till you drop, but if I don't use/need it what is the point of wasting a lot of time configuring something that on the next Eclipse Android bundle release would go to waste?

In any case what I do is run this "Search in file", for all XMLs in the project:

android:text="(?!@).*?"

Just don't forget to check the regexp check box to the right under the text field...

This simple regexp checks which of my strings are texts, and which uses the strings file...

Enjoy!

Wednesday, April 16, 2014

Android animation crops the view

I had this simple Animation XML:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="300"
        android:fromXDelta="100%"
        android:toXDelta="-10%" />
    
    <translate
        android:duration="100"
        android:fromXDelta="-10%"
        android:startOffset="300"
        android:toXDelta="0%" />
</set>

Which simply enough should have slide a view from the right, and cross the left border of the screen and then return back to its place.

Simple enough...? Nothing is simple!

As it seemed, the OS cropped the darn view, rendered it with a missing 10% of the view right edge.
I've searched and tried different approaches for hours, (... guys hours for a stupid animation!!!)

After a long while I finally found a blog post from 2011 that made some sense.

Since Chet says, and I agree that these flags, once you are aware of their existence, make clear sense of what they do, and since he explain in detail the 'why', I'm not going to, because he did it magnificently, I'll just sum it up:


fillEnable is by default 'false'
fillAfter is by default 'false', and IS NOT effected by the fillEnabled flag.
fillBefore is by default 'true', and IS effected by the filleEnabled flag.

So to conclude this, here is the solution that worked for me(Note the bold lines):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="300"
        android:fillBefore="false"
        android:fillEnabled="true"
        android:fromXDelta="100%"
        android:toXDelta="-10%" />
    
    <translate
        android:duration="100"
        android:fillBefore="false"
        android:fillEnabled="true"
        android:fromXDelta="-10%"
        android:startOffset="300"
        android:toXDelta="0%" />
</set>

Monday, March 17, 2014

Determine running OS programmatically in Java

I know that most people don't use this, but for the moment you do need it, here is a nice way that you scan detect the running OS in Java:

public enum OSType {
    Windows("win"),
    MacOS("mac", "darwin"),
    Linux("nux"),
    Other("generic");

    private static OSType detectedOS;

    private final String[] keys;

    private OSType(String... keys) {
        this.keys = keys;
    }

    private boolean match(String osKey) {
        for (int i = 0; i < keys.length; i++) {
            if (osKey.indexOf(keys[i]) != -1)
                return true;
        }
        return false;
    }

    public static OSType getOS_Type() {
        if (detectedOS == null)
            detectedOS = getOperatingSystemType(System.getProperty("os.name", Other.keys[0]).toLowerCase());
        return detectedOS;
    }

    private static OSType getOperatingSystemType(String osKey) {
        for (OSType osType : values()) {
            if (osType.match(osKey))
                return osType;
        }
        return Other;
    }
}

Use it wisely, and leave your comments below :)


Thursday, March 13, 2014

Android - Type Safe SharedPreferences

OK, so how many time did you get so pissed because of the way you had to use the SharedPreferences and store stuff on Android?

I know this drove me mad more than once, and the fact I have to copy paste some implementation from one app to another was even more frustrating...

So.......  I've thought a while back to make a generic storage utility object, specifically for Android, (I already have something like that for Pure Java) and after a long while I have. I know this is a bit overkill, but hell, I would gladly pay with few extra lines of code to save hundreds, for a readable, comfortable and quick coding.

If you find the code useful, leave me a comment... I would like to know I'm not doing this for no good reason!

For me the code is pretty obvious, but then again I've been at it for years... let me know what is not clear so I can elaborate more about these subjects.



-- UPDATE --

I've released Cyborg not too long ago, and this sort of generic shared preferences is build in and optimized further in terms of how much code you need to write, You can find it here.

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.