Wednesday, December 14, 2011

Media Scanner Applicaton


Sometimes I prefer to use adb push to send some images to my phone. To make new photos visible in Gallery we need to run media scan. One way to do it is to unmount/mount storage, but I specially use adb to avoid remounting for some purposes.

Simple application Media Scanner allows running media scan at any time.

To trigger media scan process the following intent is broadcasted:

new Intent(
                Intent.ACTION_MEDIA_MOUNTED,
                Uri.parse("file://" + Environment.getExternalStorageDirectory()))


I decided to add some animation to make application more attractive and nice-looking.

Animation in application is based on ViewFlipper class. It can automatically flip between each child at a regular interval. So we could control it using startFlipping() and stopFlipping() functions.

Application will look like at screenshots:


















I have prepared several images of circled arrows to make rotation effect.
 
Also I add widget that could be added to Home Screen.

App Widget layouts are based on RemoteViews that results in some restrictions on way to implement animation.
In my case I choose ProgressBar.

<ProgressBar
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_gravity="center"
            android:indeterminateDrawable="@drawable/widget_progress_animation"
            android:indeterminateOnly="true" />

where indeterminateDrawable is specified as 

<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/widget_refresh"
    android:pivotX="50%"
    android:pivotY="50%" />
 
and widget_refresh is png image.

RemoteViews allow controlling visibility of ProgressBar in my WidgetProvider.

I’ve faced with some issues during testing Media Scanner:

1.   During testing widget with Froyo version I’ve got the exception:
android.widget.RemoteViews$ActionException: view: android.widget.ProgressBar can't use method with RemoteViews: setVisibility(int)
 
that was fixed.

The deal was that the View.setVisibility() method is marked with the @android.view.RemotableViewMethod annotation, which is what allows it to be called through RemoteViews.  In this case, ProgressBar actually overrides the default View implementation, but without adding that annotation. This is why it throws the exception. (see http://www.mail-archive.com/android-developers@googlegroups.com/msg50097.html )

 
2.      I own Nexus One and Samsung Galaxy S II devices.
Application seems to work fine with Galaxy, but it has some strange behavior on Nexus. I have noticed that widget received broadcast intent ACTION_MEDIA_SCANNER_STARTED with some delay (about 1-2 secs).
 
12-13 13:39:44.643  3189   3189   I      BootReceiver Got intent with action android.intent.action.MEDIA_MOUNTED
12-13 13:39:44.653  3219   3219   I      MediaUploader No need to wake up
12-13 13:39:44.693  3117   3117   I      AppsOrganizerMountReceiver       android.intent.action.MEDIA_MOUNTED
12-13 13:39:44.793  3198   3271   D      MediaScannerService start scanning volume external
12-13 13:39:44.803  3152   3152   D      TEST   ACTIVITY======= onReceive ==========android.intent.action.MEDIA_SCANNER_STARTED
12-13 13:39:45.223  3198   3271   D      MediaScanner prescan time: 90ms
12-13 13:39:45.223  3198   3271   D      MediaScanner     scan time: 291ms
12-13 13:39:45.223  3198   3271   D      MediaScanner postscan time: 2ms
12-13 13:39:45.223  3198   3271   D      MediaScanner    total time: 383ms
12-13 13:39:45.223  3152   3152   D      TEST   ACTIVITY======= onReceive ==========android.intent.action.MEDIA_SCANNER_FINISHED
12-13 13:39:45.233  3198   3271   D      MediaScannerService done scanning volume external
12-13 13:39:46.243  3152   3152   D      TEST   ======= onReceive ==========android.intent.action.MEDIA_SCANNER_STARTED
12-13 13:39:46.253  3189   3189   I      BootReceiver Got intent with action android.intent.action.MEDIA_SCANNER_FINISHED
12-13 13:39:46.253  3152   3152   D      TEST   ======= onReceive ==========android.intent.action.MEDIA_SCANNER_FINISHED

I saw in logs that widget received START intent after activity (when it is in foreground) already received FINISHED. The delay can be seen also when activity is paused and does not listen for broadcast intents.
It looks like somebody in system also registered for media broadcast perform long-running operations in onReceive() method.

First of all I pay attention to the following line in log:
12-13 13:39:44.693  3117   3117   I      AppsOrganizerMountReceiver       android.intent.action.MEDIA_MOUNTED

This is log from AppsOrganizer application that I’ve installed sometimes ago. After removing it from Nexus issue disappeared. So it was the root cause of the delay.

AppsOrganizer is Open Source Project

@Override
    public void onReceive(Context context, Intent intent) {
        Log.i("AppsOrganizerMountReceiver", intent.getAction());
        try {
            // aspetto un secondo, potrebbero non essere ancora disponibili le
            // app su sd
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        DatabaseHelper dbHelper = DatabaseHelper.initOrSingleton(context);
        ApplicationInfoManager.reloadAll(context.getPackageManager(), dbHelper,
                null, false, null);
        // rimetto first time in modo da far ricaricare la lista delle
        // applicazioni
        SplashScreenActivity.firstTime = true;
    }

we could see that there is: Thread.sleep(1000) in onReceive and then some operations with DatabaseHelper that lead to delay in my widget.


Application source code: http://code.google.com/p/mediascanner/   


Android Market: https://market.android.com/details?id=com.jmlinnik.android.mediascanner 

 


No comments:

Post a Comment