Tuesday, November 21, 2017

Implementing SearchView in Toolbar with Fragments managed by your MainActivity

Google the above and you will get multiple ways that people have taken to implement search results as a fragment. One of the other pickles is passing the "results" of the search, back to the main activity.

Android wants you to use an Activity to handle the search results, but in my current app, I am using the "navigation drawer" pattern and to me it made sense to have my main activity manage the search and use fragments to display the results.

Now, I could have done this by taking control of the Search Widget and SearchView's setOnQueryTextListener. Although this works well and in some ways would be easier,  I wanted to take advantage of some of the more interesting possibilities provided by using a ContentProvider and letting the Android do the search management.

So, to implement Fragment support for the results, I did the following....


in the AndriodManifest.xml I added the following to my MainActivity:

<activity    
  android:name=".MainActivity"    
  android:launchMode="singleTask"

 <intent-filter>
        ...
        <action android:name="android.intent.action.SEARCH" />
    </intent-filter>
    <meta-data android:name="android.app.searchable"        
              android:resource="@xml/searchconfig"/>
</activity>

The android:launchMode="singleTask" causes the activity to remain the root task and more importantly the system does not recreate it if it already exists. The System routes the intent to existing instance through a call to its onNewIntent() method. The MainActivity can now handle the creating the fragment and displaying the search results

Now, inside MainActivity:

@Overrideprotected void onNewIntent(Intent intent) {
    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
        String query = intent.getStringExtra(SearchManager.QUERY);
         if(null!=query&&query.length()>0){
            // Manage search fragment here...        }
    }
}

And to set up the search bar:

 @Override   public boolean onCreateOptionsMenu(Menu menu) {
       // Inflate the menu; this adds items to the action bar if it is present.      
       getMenuInflater().inflate(R.menu.main, menu);
       MenuItem searchItem = menu.findItem(R.id.action_search);
       SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
       final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
       searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); // We are the searchable activity

       return true;
   }

Hope this helps!

Friday, December 30, 2016

Customizing an Android Button - Changing Font Color on Click?

Do you need to change the color of the text when a button is clicked? 
To do so, create a selector resource in res/color directory for the text color 
custom_text_color.xml
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/colorTextIcons" android:state_pressed="true" />
    <item android:color="@color/colorPrimaryDark" android:state_pressed="false" />
</selector>
Then add the color to the button layout:
 <Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    android:textColor="@color/custom_text_color"
    />

Sunday, May 29, 2016

How to pick multiple files from Android's gallery via API? - UNDOCUMENTED

Google "How to pick multiple files from Android's gallery" and you'll get multiple Stack Overflow answers on how to do it, and just as many complaints that the "answer" does not work. At least not on many Samsung devices.

I implemented the standard answer:
         intent = new Intent(Intent.ACTION_GET_CONTENT);  
         intent.addCategory(Intent.CATEGORY_OPENABLE);  
         intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);  
         intent.setType("image/*");  

and on my Samsung device, I still could only select ONE photo!

Opening up the default email program though, I could use Gallery to select multiple photos. WTF!

Multiple Googling and still no luck!

Finally, out of desperation I decompiled SecGallery2013.apk from my phone. Low and behold there was a undocumented Action in the manifest:

"android.intent.action.MULTIPLE_PICK"

Using this action in my Intent I managed to get Android Gallery to go into multiple selection mode. Yay!

Now, onActivityResult returns a Intent with new extras: "selectedCount" and "selectedItems".

"selectedItems" returns to us a string array list of Uri's to the pictures!

So my code now looks like this:

To call the Gallery:

     findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {  
       @Override  
       public void onClick(View v) {  
         // Undocumented way to get multiple photo selections from Android Gallery ( on Samsung )  
         Intent intent = new Intent("android.intent.action.MULTIPLE_PICK");//("Intent.ACTION_GET_CONTENT);  
         intent.addCategory(Intent.CATEGORY_OPENABLE);  
         intent.setType("image/*"); 
         // Check to see if it can be handled...
         PackageManager manager = getApplicationContext().getPackageManager();  
         List<ResolveInfo> infos = manager.queryIntentActivities(intent, 0);  
         if (infos.size() > 0) {  
           // Ok, "android.intent.action.MULTIPLE_PICK" can be handled 
           action = "android.intent.action.MULTIPLE_PICK"; 
         } else {  
           action = Intent.ACTION_GET_CONTENT;
         /* This is the documented way you are to get multiple images from a gallery BUT IT DOES NOT WORK with Android Gallery! (at least on Samsung )  
           But the Android Email client WORKS! What the f'k!  
               */  
           intent.setAction(Intent.ACTION_GET_CONTENT);  
           intent.addCategory(Intent.CATEGORY_OPENABLE);  
           intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); // Note: only supported after Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT, harmless if used below 19, but no mutliple selection supported
         }  
         startActivityForResult(intent, 0xdead);  
       }  
     });  

And in the onActivityResult:

   @Override  
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
     if(action.equals("android.intent.action.MULTIPLE_PICK")){  
       final Bundle extras = data.getExtras();  
       int count = extras.getInt("selectedCount");  
       Object items = extras.getStringArrayList("selectedItems");  
       // do somthing  
     }else {  
       if (data != null && data.getData() != null) {  
         Uri uri = data.getData();  
         // do somthing  
       } else {  
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {  
           ClipData clipData = data.getClipData();  
           if (clipData != null) {  
             ArrayList<Uri> uris = new ArrayList<>();  
             for (int i = 0; i < clipData.getItemCount(); i++) {  
               ClipData.Item item = clipData.getItemAt(i);  
               Uri uri = item.getUri();  
               uris.add(uri);  
             }  
             // Do someting  
           }  
         }  
       }  
     }  
     super.onActivityResult(requestCode, resultCode, data);  
   }  

Wednesday, May 11, 2016

Sample App - Automatic Call Recorder for Android

I thought I'd post a not quite trivial sample app for perusal, so here it is:
Automatic Phone Call Recorder for Android
An automatic phone call recorder, records ALL calls your phone gets. You can choose to not record some of your contacts, or you can record every call. Schedule recording cleanup and mark recordings to never be deleted (by the cleanup schedule).
It's up to you to determine if this is legal in your jurisdiction. Not every phone supports call recording, so your mileage may very.

Sunday, April 17, 2016

A blast from my past...

An old colleague of mine (he is in the video) posted this to FB.

Many moons ago, I was the Technical Product Manager for MKS Internet Anywhere...



Thanks Sean!

Monday, April 11, 2016

So, ever since moving from Eclipse to Android Studio, I've been annoyed by the fact that importing an old project copies all the libraries under the project directory. This makes sharing libraries a bit more difficult, since you now need to manage them in two locations.

Apparently Gradle likes dependencies is under its root, for example:
Project
  |--build.gradle
  |--settings.gradle
  |--Dependency
  |    |--build.gradle
Now in most of my cases, I have my libraries as separate projects that I DO NOT want located under the app's root. So today, I finally got off my ass to figure out how to do that. (Yes, I've been very lazy about this)

The fix it turns out is rather simple. To achieve a directory structure like this:
Project
  |--build.gradle
  |--settings.gradle
Dependency
  |--build.gradle

In the project/settings.gradle, add the following:
include ':Dependency'
project(':Dependency').projectDir = new File(settingsDir, '../Dependency/module_build_gradle_location')
If you've just finished an import, you can now manually delete the imported module and rebuild the project.

Thursday, April 7, 2016

ActivityCompat.requestPermissions : Can only use lower 8 bits for requestCode

So, my latest got'cha is that ActivityCompat.requestPermissions forces you to use a value between 0 and 255. The android developers are playing tricks with the result code, thus restricting you to this range. Now, it would be nice if this was documented in the online ActivityCompat documentation, since this is going to be rarely hit in testing.

Documenting the findings for future reference:
The following are code from android.support.v4.app.FragmentActivity
 /**
 * Modifies the standard behavior to allow results to be delivered to fragments.
 * This imposes a restriction that requestCode be <= 0xffff.
 */
@Override
public void startActivityForResult(Intent intent, int requestCode) {
    if (requestCode != -1 && (requestCode&0xffff0000) != 0) {
        throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
    }
    super.startActivityForResult(intent, requestCode);
}

@Override
public final void validateRequestPermissionsRequestCode(int requestCode) {
    // We use 8 bits of the request code to encode the fragment id when
    // requesting permissions from a fragment. Hence, requestPermissions()
    // should validate the code against that but we cannot override it as
    // we can not then call super and also the ActivityCompat would call
    // back to this override. To handle this we use dependency inversion
    // where we are the validator of request codes when requesting
    // permissions in ActivityCompat.
    if (mRequestedPermissionsFromFragment) {
        mRequestedPermissionsFromFragment = false;
    } else if ((requestCode & 0xffffff00) != 0) {
        throw new IllegalArgumentException("Can only use lower 8 bits for requestCode");
    }
}

RANGE
startActivityForResult() in FragmentActivity requires the requestCode to be of 16 bits, meaning the range is from 0 to 65535.
Also, validateRequestPermissionsRequestCode in FragmentActivity requires requestCode to be of 8 bits, meaning the range is from 0 to 255.

see: http://stackoverflow.com/questions/33331073/android-what-to-choose-for-requestcode-values

Sunday, March 13, 2016

DatePicker, Sony Xperia and dates before 1980

I had a user report that they could not set a birth date before 1980 on their Sony Xperia phone. Bit of a problem if you are older than 36.

To fix this, first add the following to where ever you are inflating your datepicker:

this.datePicker = (DatePicker) view.findViewById(R.id.datePicker);
// hack also added to styleif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    Calendar calendar1 = Calendar.getInstance();
    calendar1.set(1900, Calendar.JANUARY, 1, 0, 0, 0);
    datePicker.setMinDate(calendar1.getTimeInMillis());
}


Unfortunately, the DatePicker.setMinDate is not exposed pre HONEYCOMB so to fix those apps, you can try adding this to your base application style

<!-- Base application theme. --><style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->    <item name="android:startYear">1900</item>
</style>

Sunday, March 6, 2016

I failed sharing in Kindergarten, so when asked to do a simple sample app, I had to create myself a GitHub account. Normally I use Bitbucket due to their more commercial friendly approach.

Set up was reasonably painless and my first impressions are positive.

My challenge, as given was as follows:

Framework:

  • Android Studio 2.0+
  • Gradle build system
  • Target SDK Marshmallow
  • Min SDK Ice Cream Sandwich
  • The use of 3rd-party libraries to facilitate development is encouraged

Task


  • Create a new android project
  • The Root Activity should be a Navigation Drawer
  • The application should follow the material design principles
  • The core view of the main activity should be a Recycler View
  • The Recyler should implement some sort of custom view
  • The custom view should download and display images downloaded from the internet (imgur)

To see my sample app, CLICK HERE

Sunday, February 7, 2016

Android DialogPreference validation

I've created a custom DialogPreference for one of my apps, but the problem was, I needed to validate the supplied input. I didn't want the dialog to close when the OK button was clicked, if the input was not correct.

To do it, I did as follows, in my custom DialogPreference

@Override
protected void showDialog(Bundle state) {
    super.showDialog(state);
    final AlertDialog dialog = (AlertDialog) getDialog();
    Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
    button.setOnClickListener(new View.OnClickListener() {
         @Override         
         public void onClick(View view) {
             // Validation
             if (mNameEditText.getText().toString().isEmpty()) {
                 mNameEditText.setError(getContext().getString(R.string.pref_error_empty));
                 return;
             }
             PersonPreference.super.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
             dialog.dismiss();
         }
     }
    );
}

Thursday, January 7, 2016

Using a java library created with Android Studio, throws the following error in the build, when you include it in another project:

Error:com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)

The fix, dumb as it is, is to include the following two settings in the build.gradle file for the library, in the project.

sourceCompatibility = 1.7 
targetCompatibility = 1.7

after inclusion, it should look something like this:

apply plugin: 'java'
sourceCompatibility = 1.7
targetCompatibility = 1.7
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'junit:junit:4.12'
}

Tuesday, December 8, 2015

Setting the Mercurial remote repository in Android Studio

This took some time to figure out, after being annoyed by the fact that Android Studio doesn't save the remote URL for a push when you manually enter it.

In the projects .hg directory, create a new file named  "hgrc"  without an extension. To this file, add the following lines

[paths]
default=url_to/replace_with/your_project

That's it. Should be obvious if you are a Mercurial command line user, which I'm not.

Thursday, November 12, 2015

Java 8 and Swing redraw issues.

Ever since Java 8 got installed on my Windows 10 machine, it's been easy to completely mess up the re-draw on Java swing applications. Move the mouse over the program enough times and things go haywire.

I've noticed this in an early release of Android Studio 1.4 , a product called Freephoneline, and most noticeably, a Netbeans RCP that I wrote and sell.

It turns out that the problem is in Java 8 itself. Swing's rendering and Microsofts Direct draw do not play nice together.

To fix this problem in my Netbeans RCP product, I've added the command line parameters:

-J-Dsun.java2d.noddraw=true -J-Dsun.java2d.dpiaware=true

to my installation, which seem to fix the problem. See my previous post on where to do it.

I'm told that: -Dsun.java2d.d3d=false will also work


Monday, November 9, 2015

Customizing the Netbeans RPC Installer for Java Command Line parameters

Locate the app.conf file in "Netbeans"/harness/etc

This is used to build the installer for your RPC application.

For example, I needed more memory for my application, so I changed this:

# options used by the launcher by default, can be overridden by explicit
# command line switches
default_options="--branding ${branding.token} -J-Xms24m -J-Xmx64m"

To this:

# options used by the launcher by default, can be overridden by explicit
# command line switches
default_options="--branding ${branding.token} -J-Xms256m -J-Xmx1024m"


I've also added:
-Dsun.java2d.d3d=false
to hopefully turn off the wonky swing redrawing issues with Java 8

Wednesday, October 21, 2015

Changing the .apk file's name in Android Studio

This is here, so I can remember next time. Use it if you will!

My version of Android Studio ( 1.4 ) does not allow you to change the name of the file produced by "Generate Signed APK...". At least if it does, I have no idea where.

So, to get an .apk that says something other than "app-release.apk" you need to modify your "build.gradle (Module:app) file by including the italicized code in the code below:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
    defaultConfig {
        applicationId "ca.jlcreative.discountcalculator"
        minSdkVersion 10
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def newName = output.outputFile.name
                    newName = newName.replace("app", "$defaultConfig.applicationId")
                    output.outputFile = new File(output.outputFile.parent, newName)
                }
            }
        }
    }
    productFlavors {
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.google.android.gms:play-services-ads:7.0.0'
    compile 'org.apache.commons:commons-lang3:3.4'
    compile 'com.android.support:preference-v7:23.0.1'
}

Wednesday, June 3, 2015

Customizing the Netbeans RPC Installer for Product Version

If you build a NetBeans RCP application using the NetBeans platform the "Package As" menu option, you'll find that when you build a new version of the application, users are unable to install it. The Netbeans installer will not re-install and application and it "thinks" that the new install, is the same version as the old install. This is because of a hard-coded (1.0.0.0.0) string in the following files, which are used to create the installation package:

product.version in {nbdir}\harness\nbi\stub\ext\infra\build\products\helloworld\build.properties

and the version attribute in <create-bundle> <component in {nbdir}\harness\nbi\stub\build.xml



To make this dynamic, so you can change versions from your build, you will need to modify the following files:

in {nbdir}\harness\nbi\stub\ext\infra\build\products\helloworld\build.properties

change 67 to:

#Changed from the hard coded "1.0.0.0.0"
product.version={product-version}


on line 166 in {nbdir}\harness\nbi\stub\build.xml


<component uid="${main.product.uid}" version="${product-version}"/> <!-- changed version to version="${product-version}" from hard coded string version="1.0.0.0.0"-->


In template.xml:

after line 141, 131 and 122 add the following:

<replacefilter token="{product-version}"     value="${product-version}"/> <!-- Added to do the substitution -->

after line 84:

<property name="product-version"  value="${suite.props.app.version}"/>


And finally, in {project}\nbproject\project.properties"

add:
# application / product version MUST be formated as N.N.N.N.N
app.version=15.0.0.0.0


Since you've changed Netbeans "installed" code, you might want to place it under version control.

This shouldn't be so hard. But like everything else in Netbeans, there you have it. Your new install will over-right your old ones now.



Wednesday, April 1, 2015

Eclipse with Android Studio Hangs

Is Eclipse with Android Studio installed failing to load?

Is it hanging somewhere loading an Android component?

Try unplugging your Android device you are using to debug from the USB cable.

Works for me, every time ;)

Friday, August 29, 2014

Switching an Android Menu Icon via Themes

First, edit attrs.xml to include the name:  <attr name="undo_icon"

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="undo_icon" format="reference" />
</resources>

Then, edit the styles.xml to define the style: <item name="undo_icon">

<resources>
    <!--
        Base application theme, dependent on API level. This theme is replaced
        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
    -->
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
        <!--
            Theme customizations available in newer API levels can go in
            res/values-vXX/styles.xml, while customizations related to
            backward-compatibility can go here.
        -->     
    </style>

    <!-- Application theme. -->
    <style name="AppThemeLight" parent="AppBaseTheme">
        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
        <item name="undo_icon">@drawable/halo_dark_content_undo</item>       
    </style>
    <style name="AppThemeDark" parent="AppBaseTheme">
        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
        <item name="undo_icon">@drawable/halo_light_content_undo</item>
    </style>
</resources>

Finally, define the menu.xml: android:icon="?undo_icon"

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/menu_undo"
        android:icon="?undo_icon"
        android:title="Undo"
        android:titleCondensed="Undo">
    </item>
</menu>

In the Manifest: android:theme="@style/AppThemeLight" >  OR switch dynamically via Context.setTheme

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppThemeLight" >


To reference in code:

        TypedValue typedValue = new TypedValue();
        getTheme().resolveAttribute(R.attr.
undo_icon, typedValue, true);
        btnUndo.setImageResource(typedValue.resourceId);


Thursday, August 28, 2014

How to fix Android SDK Content Loader stuck at 0% in Eclipse

Have you encountered the case where Eclipse hangs upon startup, in particular when you are developing an Android applications with Android SDK?

When that happens you see "Android SDK Content Loader" stuck at 0% in the bottom right hand of the Eclipse status bar.

There are four things you can try...

Solution 1:
  1. Make sure that eclipse is not active. If it is active kill eclipse from the processes tab of the task manager 
  2. Check if the adb process is running. If so, kill the adb process, and restart Eclipse. 

 Solution 2: (Works best for me, and likely the safest)
  1.  Make sure that eclipse is not active. If it is active kill eclipse from the processes tab of the task manager
  2. From the command line run: C:\eclipse\eclipse.exe -clean

Solution 3:
  1. Make sure that eclipse is not active. If it is active kill eclipse from the processes tab of the task manager
  2. Open %USERPROFILE%/ (You can locate this folder from desktop) (or paste it into Explorer on windows)
  3. Go to .android folder (This may be a hidden folder)
  4. Delete the folder "cache" which is located inside .android folder
  5. Delete the file ddms.cfg which is located inside .android folder
  6. Start Eclipse
Solution 4: (Most drastic, and one I have not tried)

Go to your workspace directory \workspace\.metadata\.plugins\org.eclipse.core.resources\\.projects

  1. Copy .projects folder to make a temporary backup.
  2. Now Delete .projects folder from workspace directory. (you will not loose your projects)
  3. Start Eclipse and wait for all progress ends at right/bottom corner. Once completed all processes, shutdown Eclipse.
  4. Paste .projects folder which you have backup earlier to \workspace\.metadata\.plugins\org.eclipse.core.resources\ directory. Overwrite existing .projects folder.
  5. Start Eclipse again. And all will work.
In above scenario Eclipse will automatically find your earlier projects. You do not have to import them manually.

Good Luck

Wednesday, July 2, 2014

NetBeans RCP installer version upgrade

So every other installation package I've ever used, allows you to upgrade your application, by changing its version number.

Not so with the NetBeans packager.

The NetBeans platform installer uses common code located in {nbdir}\harness\nbi\stub
Customizing these files and sources allows you to brand you installer, which is all nice, but you would not expect to have to customize these files in order to get your program to upgrade.

Product version is controlled in two files, both which need to be changed.
  1. product.version in {nbdir}\harness\nbi\stub\ext\infra\build\products\helloworld\build.properties
  2. version attribute in create-bundle > component in {nbdir}\harness\nbi\stub\build.xml 
These values must be changed, in order for the installer to allow the installation.

Stupid, but there you have it. Some day, when I have time, I'll try and automate it all.