I just published a small tool to manipulate Google Calendar entries from the command line while being to able to mass- / batch-process several entries at once. For details see this GitHub repo.
While I was writing this tool a had a chance to learn picocli and the Google API Java client.
Thomas M. Hofmann's Blog
dig deeper
Tuesday, July 16, 2019
Sunday, November 16, 2014
Introducing Xposed Media Scanner Optimizer for Android
Let me introduce my new Xposed Module. It allows you to customize the way the media scanner works on Android.
It solves several problems that I have with the media scanner. As Xposed module developer one needs to reboot the device quite often. The media scanner runs on each reboot and eating much battery as the device stays awake while the scanner is running and doing lots of I/O.
For non developer the Xposed Media Scanner Optimizer also provides useful functionality.
First of all let me explain how the media scanner works on Android. Each time the device has bootet it runs and scans all volumes/storage. Depending on the size of the storage available on the device and the SD Card and the number of files this usually takes between 5 and 20 minutes. For each file and directory it finds it creates an entry in the so-called Android Media Store. For special file types like images, videos and music files it also scans for meta data like EXIF information for images and mp3 tags for music files. Applications may query the Media Store to find specific files bases on the file type or meta data. Well known applications that do so are the Gallery and the Music Player. Other applications may also do so.
The media scanner runs as a background service and is not visible to the user.
The Xposed Media Scanner Optimizer provides the following tweaks to the media scanner:
- The service can be run as a foreground service allowing the user to see when and how long it runs. Foregound services are visible to the user via a notification.
- When the scanner has finished details of how long each scan took are shown using a notification.
- The scanner can be run in a mode where only specific directories will be scanned. This reduces scan times and thus saves battery. It also allows you to control what shows up in the Gallery or Music Player apps.
- It is possible to tell the scanner that only specific file types (e.g. music files) should be scanned on a directory basis.
- It allows you to completely delete the contents of the media store.
- It allows you to trigger a scan whenever you want to
- On some Android phones the scanner is scheduled to run several times after a boot which is completely unnecessary and in my opinion is a bug. These repetitive scans can be prevented.
The directories that contain my music files also contain images for the album and the artist. These files would normally show up in the Gallery. There, I only want to see the contents of the directories where I keep my pictures. I don't want to have the gallery cluttered with album artwork or images of artists from the music directories.
The Xposed Media Scanner Optimizer settings application can be started from the launcher and allows you to configure the following settings:
- Repetitive Scans: If this preference is checked repetitive scans at boot time will be prevented. Repetitive scans are not necessary and only cause greater battery drain. Depending on the Android version and vendor repetitive scans may occur and can be prevented by checking this preference.
- Service Importance: When checked the media scanner will run as foreground service and its operation will be visible to the user via a notification.
- Thread Priority: If checked the media scanner will be forced to run with background thread priority even if it is running as a foreground service. This is the suggested setting. If unchecked the thread priority depends on whether it runs as a foreground service or not.
- Directories: If checked the media scanner will only scan directories that have a .scanMedia file (i.e. an empty file with a dot as first part of the name similar to the .noMedia file). Use a file explorer app to create this file. This file can be placed in a directory that may contain subdirectories. The subdirectories will be scanned as well. If unchecked all directories will be scanned (except for the ones containing a .noMedia file - this default behavior of the media scanner is not altered by the Xposed Media Scanner Optimizer).
- Media Types: If checked only certain file types will be scanned. To define which file types the scanner will scan in a directory create the following files: .scanMusic, .scanVideo and .scanPictures. So for a directory containing a collection of music albums you would need to create a .scanMusic file at the directory that contains all albums if you only want the music files scanned. Images contained in the album directories would then be skipped.
- Result Notifications: If checked a notification will be shown when the scanner has finished including information about the scan time.
- Trigger Media Scanner: When touched a media scanner is triggered.
- Delete Media Store Contents: When touched the contents of the media store are deleted. This will not delete the actual files on the storage. Trigger a scan or reboot to let the scanner populate the media store after deleting its contents. This is useful if the media stores already contains entries that you do not want like images from all directories in the Gallery. First check the preferences to restrict what will be scanned, empty the media store and trigger a rescan. After the rescan only what has been scanned depending on your preferences will appear in the Gallery.
- Android Logging: If this preference is checked details of the media scanner and the Xposed module are written to the Android log. This is useful for development and to troubleshoot. For end users this should stay unchecked.
- Xposed Log: If this preference is checked details will be written to the Xposed log. Since the Xposed log is persisted this is also useful for troubleshooting. For end users this should stay unchecked.
The Xposed module is available from the Xposed repository. Please use this XDA Developer forum thread for feedback and discussions.
It should work on Android 4.1.2 (API 16) and up.
If you like the module feel free to donate :)
Saturday, July 5, 2014
Fixing random reboots on Samsung Kitkat Firmwares like I9505XXUGNF1
After updating my SGS 4 to I9505XXUGNF1 I like many others experienced random crashes which at first glance seemed to be reboots of the device. Looking at the logcat I figured out that actually the system server process crashes due to an ArrayIndexOutOfBoundsException. After reading the source code I figured what the problem is and wrote this small module to workaround the crash. My S4 is running without a single crash since.
Here are some technical details:
The logcat shows:
06-27 16:29:22.471 E/AndroidRuntime(28244): !@*** FATAL EXCEPTION IN SYSTEM PROCESS: ActivityManager
06-27 16:29:22.471 E/AndroidRuntime(28244): java.lang.ArrayIndexOutOfBoundsException: length=14; index=-1
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ProcessList.computeNextPssTime(ProcessList.java:580)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.requestPssAllProcsLocked(ActivityManagerService.java:16951)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.updateOomAdjLocked(ActivityManagerService.java:17766)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.trimApplications(ActivityManagerService.java:17831)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor.activityIdleInternalLocked(ActivityStackSupervisor.java:2992)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.activityIdleInternal(ActivityStackSupervisor.java:3961)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.handleMessage(ActivityStackSupervisor.java:3983)
06-27 16:29:22.471 E/AndroidRuntime(28244): at android.os.Handler.dispatchMessage(Handler.java:102)
06-27 16:29:22.471 E/AndroidRuntime(28244): at android.os.Looper.loop(Looper.java:157)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService$AThread.run(ActivityManagerService.java:2376)
The problem is in the method computeNextPssTime.
The last statement in the method accesses either the array named sFirstAwakePssTimes or sSameAwakePssTimes. An ArrayIndexOutOfBoundsException happens when the parameter procState is wrong. My first guess was that -1 is used as index into the array.
Looking at the call stack one can see that the parameter is a value taken from an instance of the class ProcessRecord. The value is initialized with -1 when that instance is created.
Somehow this invalid value for the procState is being used and finally ends up being used as the index into the array.
I don't know what causes this (could be timing issues or some changes that Samsung made). Nevertheless, it is possible to prevent the system server to crash by changing what the computeNextPssTime method does.
Using the amazing Xposed Framework I created an Xposed module that will check the procStat parameter value and in case of an invalid valued (like -1) will return a default value for the result of the method. This works pretty well to workaround the problem. Of course, it does not fix the actual problem but my phone is running well since then.
Whenever -1 or any other invalid value is passed to computeNextPssTime a message is logged to Android's log with the tag SSCF. Using logcat you can see how often this happens and how often the system server would crash if not using the module.
Since the problematic code is in AOSP I wonder why this seems to happen on newer Samsung Kitkat ROMs only. To me the problem seems to occur with an increasing chance the more processes are running. Maybe this is why users are starting to see random reboots only after a few days (when more apps might have been installed). At least this is what happened to me.
Discussion can be found on this XDA forum thread.
Here are some technical details:
The logcat shows:
06-27 16:29:22.471 E/AndroidRuntime(28244): !@*** FATAL EXCEPTION IN SYSTEM PROCESS: ActivityManager
06-27 16:29:22.471 E/AndroidRuntime(28244): java.lang.ArrayIndexOutOfBoundsException: length=14; index=-1
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ProcessList.computeNextPssTime(ProcessList.java:580)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.requestPssAllProcsLocked(ActivityManagerService.java:16951)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.updateOomAdjLocked(ActivityManagerService.java:17766)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.trimApplications(ActivityManagerService.java:17831)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor.activityIdleInternalLocked(ActivityStackSupervisor.java:2992)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.activityIdleInternal(ActivityStackSupervisor.java:3961)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.handleMessage(ActivityStackSupervisor.java:3983)
06-27 16:29:22.471 E/AndroidRuntime(28244): at android.os.Handler.dispatchMessage(Handler.java:102)
06-27 16:29:22.471 E/AndroidRuntime(28244): at android.os.Looper.loop(Looper.java:157)
06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService$AThread.run(ActivityManagerService.java:2376)
The problem is in the method computeNextPssTime.
The last statement in the method accesses either the array named sFirstAwakePssTimes or sSameAwakePssTimes. An ArrayIndexOutOfBoundsException happens when the parameter procState is wrong. My first guess was that -1 is used as index into the array.
Looking at the call stack one can see that the parameter is a value taken from an instance of the class ProcessRecord. The value is initialized with -1 when that instance is created.
Somehow this invalid value for the procState is being used and finally ends up being used as the index into the array.
I don't know what causes this (could be timing issues or some changes that Samsung made). Nevertheless, it is possible to prevent the system server to crash by changing what the computeNextPssTime method does.
Using the amazing Xposed Framework I created an Xposed module that will check the procStat parameter value and in case of an invalid valued (like -1) will return a default value for the result of the method. This works pretty well to workaround the problem. Of course, it does not fix the actual problem but my phone is running well since then.
Whenever -1 or any other invalid value is passed to computeNextPssTime a message is logged to Android's log with the tag SSCF. Using logcat you can see how often this happens and how often the system server would crash if not using the module.
Since the problematic code is in AOSP I wonder why this seems to happen on newer Samsung Kitkat ROMs only. To me the problem seems to occur with an increasing chance the more processes are running. Maybe this is why users are starting to see random reboots only after a few days (when more apps might have been installed). At least this is what happened to me.
Discussion can be found on this XDA forum thread.
Wednesday, October 16, 2013
Using Command Parameter Value Convertes with Eclipse e4
This post describes how value converters for command parameters can be used in e4.
In e4, commands can have parameters. Each parameter has a value. When you specify theses values in the application model they are all strings.
Suppose you want to pass an Integer value for a command parameter. In your handler you would like to get the value injected as an Integer instance so that you do not need to perform the conversion in the handlers @CanExecute or @Execute method.
The necessary conversion can be done by the e4 framework before the handler is invoked. Take the following approach.
In e4, commands can have parameters. Each parameter has a value. When you specify theses values in the application model they are all strings.
Suppose you want to pass an Integer value for a command parameter. In your handler you would like to get the value injected as an Integer instance so that you do not need to perform the conversion in the handlers @CanExecute or @Execute method.
The necessary conversion can be done by the e4 framework before the handler is invoked. Take the following approach.
Implement the converter
Create a new class that inherits from org.eclipse.core.commands.AbstractParameterValueConverter. Implement the abstract methods. These are:
Object convertToObject(String parameterValue) and String convertToString(Object parameterValue)
In the convertToObject method convert the string value of the command parameter to an instance of the type you require. For String to Integer conversion this could be done by simple calling Integer.valueOf(parameterValue). The signature of the method declares to throw ParameterValueConversionException in case the conversion fails. Create an instance of this exception and throw it in case the parameter value cannot be converted. Unfortunately, this exception is currently (Kepler 4.3.1) swallowed in the eclipse e4 framework so you won't have any idea why things are not working. You might want to log the problem for this reason right in the converter.
Now comes the tricky part. The convertToString method does not do the reverse of the convertToObject method (I thought so at first). It is called with the string value and needs to return a string. For simple String to Integer conversion you can just return the same string that is passed to the method. You might want to treat a null value different and return the string "null" for example.
Register the converter
In the bundle that supplies the value converter extend the org.eclipse.ui.commands extension point and add a commandParameterType.
The commandParameterType has three properties: id, type and converter.
Choose an id that is specific to your bundle name. For the type enter the class name of the objects you want to convert the string value to, e.g. java.lang.Interger. The converter property takes the fully qualified class name of the class that is called to convert the value (the one you implemented before). The class must be in a package that is exported by the bundle.
Specify the TypeID for the Command Parameter
The last step is to specify the TypeID for the command parameter in the application model. Each command parameter has a property TypeID that usually does not need to be filled with a value. If you want to make use of the converter just registered enter the ID of the commandParameterType you registered through the extension point above. Do not enter the type of the objects you want to convert to (e.g. java.lang.Integer).
Now the command parameter values should be converted automatically. Make sure to adjust the signature of your handler methods so that they are found and called.
For debugging purposed you can set a breakpoint in org.eclipse.core.commands.ParameterizedCommand.generateCommand(Command, Map) and of course in your converter class.
Sunday, April 15, 2012
IBM ED Search for Android - Part 4
Change Indicators
A result of a search or the list of persons in a bluegroup might change over time. If you trigger a reload of the search result new persons might be added, others might be removed.To better understand these changes the app uses change indicators. These are narrow colored lines to the left of the person picture. New persons in a search result will have a green line, persons removed will have a red line and persons where the information in the directory has changed since the last time it was loaded will have a yellow line.
It should be pointed out that persons that are no longer part of the search result will not be deleted from the device. Instead they are marked with the red change indicator. This has the following advantages:
- If you still want to have that person record available in the app you can choose to star the person and it will be available under All Starred Persons
- You can see who has been removed from a bluegroup
In case the change indicators are not confirmed and the search result is reloaded again new change indicators will be added to the existing ones.
Therefor, it is possible that more than one change indicator will be shown for a person. When a person was added to a bluegroup for example and later the information about that person changed in the directory a green and yellow change indicator will be shown. Likewise, when the person was first added and then later removed a green and red change indicator will appear.
Note that persons marked with a red change indicator might have left the company and are no longer in the directory. When you try to load the person details for such a person the app will display a short message that the person could not be found in the directory.
You can also apply a filter for change indicators to restrict the results shown to only new, changed or deleted persons. A future post will cover filtering.
Labels:
android,
bluegroups,
bluepages,
ibm_ed_search
Saturday, April 14, 2012
IBM ED Search for Android - Part 3
Shortcuts to starred search results
When the app is launched the list of starred search results is immediately visible. Touching one the entries in the list opens the search result view.Yet, there is a quicker way to access your most often needed search result(s). This could be the list of members of a bluegroup you frequently need to access in order to call someone.
Android has the concept of shortcuts that can be placed directly on the launcher's home screen. Such a shortcut is able to launch into a specific activity in the app.
You can create a shortcut on the launcher's screen for any starred search result. Tapping the shortcut will immediately load the search result and save a little bit of time when looking up contacts.
To create a shortcut a search result must be starred and thus be available for offline use.
Each launcher has a different but similar method to create a shortcut. Usually, you can either use the menu or long press the launcher's home screen background to create a shortcut. On Ice Cream Sandwich (Android 4.x) shortcuts can also be created from the Widgets section in the app drawer.
So start to create a shortcut for the IBM ED Search app by dragging the widget to the launcher's home screen.
As soon as the widget is dropped the IBM ED Search app will open an activity to choose the starred search result for which the shortcut is to be created.
On this screen the name of the shortcut can be changed. By default, the value for the name is the same as it is shown in the list of starred search result.
There is also an option to force reloading of the search result when it is opened using the shortcut.
Customize the name if you like and decide whether the result should be reloaded and then tap on one of the entries in the list. This will create the shortcut.
For different kind of search results different icon overlays are used. A shortcut for the members of a bluegroup will have a different icon than the result for a management chain query.
Deleting a shortcut from the launcher will not in any way delete the app or the data that is stored for it.
Labels:
android,
bluegroups,
bluepages,
ibm_ed_search
Thursday, April 12, 2012
IBM ED Search for Android - Part 2
This second blog post will focus on the Person Details Screen
When you touch a person entry in a search result the person details screen / view will open showing more information about the person you selected. When the details screen is opened for the first time for a person the app will try to download additional information about the person (department, secretary, etc.). Once this information has been downloaded it will be persisted and will be available offline. Notice how the number of entries in the details list grows when new data is downloaded.In case the details screen is opened and no connectivity to the directory server can be established only the information that is already loaded will be available. The next time the details screen is opened or when you trigger the reload action the app will again try to download all details.
The details screen shows the person's bluepage picture, the name, job description and a list of details. The entries in the list of details show icons that can be touched to trigger an action associated with the detail.
For example, clicking the icon to the right hand side of the department detail will trigger a search for all persons in that department. Touching the icon on the email address detail will open the email application with the email address filled in as recipient. For the primary email address there is an action to start a Sametime chat with the person. From the manager detail you can either open the details for the person's direct manager or you can search for the complete management chain. You can also dial a telephone number this way or start google maps to display the work location or start the google maps navigation.
Additional actions are available at the bottom of the screen in the action bar. You can start or reload all the information about a person for example. Reloading will also try to reload the person picture. Remember that you can long press an icon on the action bar to show a tool tip that further explains what the action will do.
Since not all available actions for a person are shown in the action bar check out the additional actions available when you press the menu button or the action overflow (depending on whether your Android device has a menu button or not). Currently these actions are "Bluepages" and "IBM Connections" which will launch the browser and open up the associated person profile in either Bluepages or IBM Connections.
Once you navigate away from the person details screen (e.g. by triggering a search for the members of the same department or the secretary information) the app will either show a list of search results in the former case or navigate directly to another person details screen in the later case. Using the back button you will return to the person details screen of the person you began with.
You can always hit the icon on the left of the upper action bar to return to the home screen to conduct a new search. If you do so the history will be cleared and the back button will no longer bring you back to the previous screen.
Long pressing / touching an entry in the details list opens up a context menu where you can choose to copy the text of the detail to the clipboard. This can be handy to copy the address for example.
undefined
Labels:
android,
bluegroups,
bluepages,
ibm_ed_search
Subscribe to:
Posts (Atom)