B4A Library MSDynamicGridView Library

This library implements one kind of GridViews:

Android Version Support
This GridView doesnt work 100% on Andriod 2.3 and lower. The animations are not supported and also the events are not raised (due to a bug).

1. DynamicGridView
This gridview is very flexible. All items must be of the same height/width.
It can load layouts into a panel, so you can have different layouts in each grid cell. It is very flexible and powerful. More features include the ability to drag/drop.

Credits:
This is very important. Most of these libraries are based on the work and research of warwound. Also, I got some ideas from Informatix. But warwound's custom listview library and explanations are the reason there libraries are here, if you are not going to donate to me, I appreciate donating to him.

Usage:
The gridview is not as simple to use like the B4A Listview.
It is more complex and rely on the concept of Adapters. If you have used Informatix's ULV you will be more familiar with the format.

Databinding:
The Gridview itself does not hold any data (String/Int/Customtype anything).
As a user it is your job to bind the data to a cell in a gridview using an ItemID or a ItemTag.
An ItemID is a unique ID for each cell of the Grid. This is generated internally.
An ItemTag is also unique for each item but it allows you to pass more complex data. This can allow you to change the appearance or layout of items by passing some tags.

This is where we get to the concept of an Adapter.

Adapters:
The adapter is responsible to create a layout and fetch the data to display in the views of the listview. Since listviews/gridviews can scroll and hold large amounts of data, the Adapters can recycle items into and out of the view.

MSDynamicAdapter:
The adapter needs to know the number of columns and width and height of each cell on initialization.
B4X:
DynamicAdapter.Initialize("DA",NumCols, 100%x/NumCols, 100dip)
You can add items to the Adapter at any time:
B4X:
    For i = 0 To 50
        DynamicAdapter.Add("Item " & i)
    Next
The ItemTag does need to be unique but we also internally always generate a unique ItemID. You can use ItemTag to make certain cells look similar, or hold certain kinds of data.

The Adapter will raise two events:
- GetLayout
- GetContent

In GetLayout you must create a new panel, load the layout by code or designer, and return the panel.
You should not really bind any content to your views here.

In GetContent you will get the ContentPanel in the parameters. Use ContentPanel.GetView to find your labels and imageviews etc, and then assign the values here like label.text and imageview.background etc.

It is important to understand adapters before you really move on.

Adapter Events Signature:
GetLayout
(Position as Int, ItemID as Long , ItemTag as String) as Panel
Create a panel in this event and return it, that will be used as the layout for the cell.
You can use Postion/ItemID/ItemTag to differenciate the layout.

GetContent (Position as Int , ItemID as Long , ItemTag as String , ContentPanel as Panel) as Panel
In this event you can used ItemID/ItemTag/Position to pull your data (from a list/map/db) and put that data onto the views in ContentPanel. You can find the views using GetView, or maybe use Tags.

GridView:

The Gridview is very simple. Just initialize it with an eventname, and add it to your activity.
You can set the adapter with:
B4X:
'Set the Gridview's adapter
    DynamicGrid.Adapter = DynamicAdapter.GetAdapter
This allows you to quickly change all the data the grid is holding!

Set the NumColums(DynamicGrid) the same as that set for the adapter.

Screenshots:
1127vxl.jpg


2r78rx2.jpg


15heghv.jpg


Samples:
Drag drop demo: https://www.b4x.com/android/forum/threads/drag-and-drop-gridview.46242/#content
Public demo: https://play.google.com/store/apps/details?id=com.maximussoft.picpuzzle

<to be continued>
 
Last edited:

Dave O

Well-Known Member
Licensed User
Longtime User
@thedesolatesoul , did you ever get selection working in this library?

I was hoping to use MSGridView to select an item (which then appears selected), then click OK.

I suppose I could just act on the single click instead, but thought I'd ask anyway. :)

Cheers!
 

Dave O

Well-Known Member
Licensed User
Longtime User
Its been a while so i have to try to remember. I dont think i got selection to work on all versions of android and the behaviour was too erratic to be released.
No worries, got it working with the single click, and the dialog closes immediately, so no selection needed in this case. :)
 

Kwame Twum

Active Member
Licensed User
Longtime User
Is it possible to track the scroll position?
I would like to know when the user has scrolled to the bottom nd load more items.

Any help is appreciated.
 

lomosami

Member
Licensed User
Longtime User
Hello,
I am trying this library with b4a 5.80. I have some difficulties in debuggin the example attached at the library: when I put breakpoint in
Sub DA_GetLayout(Position As Int, ItemID As Long, ItemTag As String) As Panel, the debug stop to continue. Is it a mine strange behaviur or other people had the same problem? Or is it normal using adapter?
Also putting breakpoint in Sub DG_ItemClick(Position As Int) I have some problem.
In release mode all is ok but I can't do debug step by step.

Thanks in advance
 

lomosami

Member
Licensed User
Longtime User
Hello,
Is it possible to change the property of the grid when clicked on? For example if the grid contain a panel with image, is it possible to load another image?
The goal is to have an image when the grid is pressed and another image when is not pressed.
Thanks

And is it possible to enable/disable the single element in grid?
 
Last edited:

lomosami

Member
Licensed User
Longtime User
Hello,
Is it possible to change the property of the grid when clicked on? For example if the grid contain a panel with image, is it possible to load another image?
The goal is to have an image when the grid is pressed and another image when is not pressed.
Thanks

And is it possible to enable/disable the single element in grid?

For the question 1: I find a solution. In click grid event I change some map used to differentiate the grid layout and after this set NotifyDataSetChanged to load the new data content.

For disable and enable single grid I think it is not possible, but nevermind..
 

cxdzbl

Active Member
Licensed User
This library implements one kind of GridViews:

Android Version Support
This GridView doesnt work 100% on Andriod 2.3 and lower. The animations are not supported and also the events are not raised (due to a bug).

1. DynamicGridView
This gridview is very flexible. All items must be of the same height/width.
It can load layouts into a panel, so you can have different layouts in each grid cell. It is very flexible and powerful. More features include the ability to drag/drop.

Credits:
This is very important. Most of these libraries are based on the work and research of warwound. Also, I got some ideas from Informatix. But warwound's custom listview library and explanations are the reason there libraries are here, if you are not going to donate to me, I appreciate donating to him.

Usage:
The gridview is not as simple to use like the B4A Listview.
It is more complex and rely on the concept of Adapters. If you have used Informatix's ULV you will be more familiar with the format.

Databinding:
The Gridview itself does not hold any data (String/Int/Customtype anything).
As a user it is your job to bind the data to a cell in a gridview using an ItemID or a ItemTag.
An ItemID is a unique ID for each cell of the Grid. This is generated internally.
An ItemTag is also unique for each item but it allows you to pass more complex data. This can allow you to change the appearance or layout of items by passing some tags.

This is where we get to the concept of an Adapter.

Adapters:
The adapter is responsible to create a layout and fetch the data to display in the views of the listview. Since listviews/gridviews can scroll and hold large amounts of data, the Adapters can recycle items into and out of the view.

MSDynamicAdapter:
The adapter needs to know the number of columns and width and height of each cell on initialization.
B4X:
DynamicAdapter.Initialize("DA",NumCols, 100%x/NumCols, 100dip)
You can add items to the Adapter at any time:
B4X:
    For i = 0 To 50
        DynamicAdapter.Add("Item " & i)
    Next
The ItemTag does need to be unique but we also internally always generate a unique ItemID. You can use ItemTag to make certain cells look similar, or hold certain kinds of data.

The Adapter will raise two events:
- GetLayout
- GetContent

In GetLayout you must create a new panel, load the layout by code or designer, and return the panel.
You should not really bind any content to your views here.

In GetContent you will get the ContentPanel in the parameters. Use ContentPanel.GetView to find your labels and imageviews etc, and then assign the values here like label.text and imageview.background etc.

It is important to understand adapters before you really move on.

Adapter Events Signature:
GetLayout
(Position as Int, ItemID as Long , ItemTag as String) as Panel
Create a panel in this event and return it, that will be used as the layout for the cell.
You can use Postion/ItemID/ItemTag to differenciate the layout.

GetContent (Position as Int , ItemID as Long , ItemTag as String , ContentPanel as Panel) as Panel
In this event you can used ItemID/ItemTag/Position to pull your data (from a list/map/db) and put that data onto the views in ContentPanel. You can find the views using GetView, or maybe use Tags.

GridView:

The Gridview is very simple. Just initialize it with an eventname, and add it to your activity.
You can set the adapter with:
B4X:
'Set the Gridview's adapter
    DynamicGrid.Adapter = DynamicAdapter.GetAdapter
This allows you to quickly change all the data the grid is holding!

Set the NumColums(DynamicGrid) the same as that set for the adapter.

Screenshots:
1127vxl.jpg


2r78rx2.jpg


15heghv.jpg


Samples:
Drag drop demo: https://www.b4x.com/android/forum/threads/drag-and-drop-gridview.46242/#content
Public demo: https://play.google.com/store/apps/details?id=com.maximussoft.picpuzzle

<to be continued>
I loaded after the completion of the layout, how to dynamically change the background picture of the specified grid, for example, I from the server to download a picture is loaded to the specified grid, rather than the completion of all the picture to download after a loading.
 

JohnC

Expert
Licensed User
Longtime User
I am getting this CRASH error most but not all the time - so it seems like some timing issue...

Basically, this error is occurring after my app is closing a secondary activity and the focus is returning to the Main activity (this grid view is located on a panel of the main activity).

Here is a screen show of the line of the error - it's on the event sub line itself - BEFORE any of my code is executed - so it seems like an issue inside the grid:

gridcrash.png


And here is the log of the error:

B4X:
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Audio Source: 1
** Activity (main) Resume **
ActionBar_MenuItemClick event fired, id:1
** Activity (main) Pause, UserClosed = false **
** Activity (comment) Create, isFirst = true **
** Activity (comment) Resume **
AB_Menu
** Activity (comment) Pause, UserClosed = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
Error occurred on line: 656 (Main)
java.lang.NullPointerException
    at com.omnisoft.voiceit.main._da_getcontent(main.java:1266)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:703)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:337)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:247)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:134)
    at com.maximussoft.gridviews.B4ADynamicGridAdapter.getView(B4ADynamicGridAdapter.java:66)
    at android.widget.AbsListView.obtainView(AbsListView.java:2597)
    at android.widget.GridView.onMeasure(GridView.java:1043)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChild(ViewGroup.java:4892)
    at android.view.ViewGroup.measureChildren(ViewGroup.java:4869)
    at anywheresoftware.b4a.BALayout.onMeasure(BALayout.java:64)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChild(ViewGroup.java:4892)
    at android.view.ViewGroup.measureChildren(ViewGroup.java:4869)
    at anywheresoftware.b4a.BALayout.onMeasure(BALayout.java:64)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2418)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2129)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1282)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1493)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1179)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4861)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
    at android.view.Choreographer.doCallbacks(Choreographer.java:562)
    at android.view.Choreographer.doFrame(Choreographer.java:532)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
    at android.os.Handler.handleCallback(Handler.java:725)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:5293)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
    at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
    at dalvik.system.NativeStart.main(Native Method)
java.lang.NullPointerException
    at android.widget.AbsListView.obtainView(AbsListView.java:2599)
    at android.widget.GridView.onMeasure(GridView.java:1043)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChild(ViewGroup.java:4892)
    at android.view.ViewGroup.measureChildren(ViewGroup.java:4869)
    at anywheresoftware.b4a.BALayout.onMeasure(BALayout.java:64)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChild(ViewGroup.java:4892)
    at android.view.ViewGroup.measureChildren(ViewGroup.java:4869)
    at anywheresoftware.b4a.BALayout.onMeasure(BALayout.java:64)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4921)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2418)
    at android.view.View.measure(View.java:16047)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2129)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1282)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1493)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1179)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4861)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
    at android.view.Choreographer.doCallbacks(Choreographer.java:562)
    at android.view.Choreographer.doFrame(Choreographer.java:532)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
    at android.os.Handler.handleCallback(Handler.java:725)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:5293)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
    at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
    at dalvik.system.NativeStart.main(Native Method)
 

JohnC

Expert
Licensed User
Longtime User
The above error was on a 4.2.2 phone.

I also ran the app on a 5.1.1 phone and the first line of the error log was different:

B4X:
LogCat connected to: URX0215A20000863
--------- beginning of system--------- beginning of main~i:** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Audio Source: 1~i:** Activity (main) Resume **
ActionBar_MenuItemClick event fired, id:1
** Activity (main) Pause, UserClosed = false **~i:** Activity (comment) Create, isFirst = true **
** Activity (comment) Resume **AB_Menu
** Activity (comment) Pause, UserClosed = true **~i:** Activity (main) Resume **~i:** Activity (main) Pause, UserClosed = false **
main_da_getcontent (java line: 672)
java.lang.NullPointerException: Attempt to read from field 'anywheresoftware.b4a.BA com.omnisoft.voiceit.main.activityBA' on a null object reference
    at com.omnisoft.voiceit.main._da_getcontent(main.java:672)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
    at com.maximussoft.gridviews.B4ADynamicGridAdapter.getView(B4ADynamicGridAdapter.java:66)
    at android.widget.AbsListView.obtainView(AbsListView.java:2369)
    at android.widget.GridView.onMeasure(GridView.java:1060)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewGroup.measureChild(ViewGroup.java:5519)
    at android.view.ViewGroup.measureChildren(ViewGroup.java:5496)
    at anywheresoftware.b4a.BALayout.onMeasure(BALayout.java:64)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewGroup.measureChild(ViewGroup.java:5519)
    at android.view.ViewGroup.measureChildren(ViewGroup.java:5496)
    at anywheresoftware.b4a.BALayout.onMeasure(BALayout.java:64)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5548)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
    at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5548)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1467)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:747)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:638)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5548)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5548)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1467)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:747)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:638)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5548)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2639)
    at android.view.View.measure(View.java:17623)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2081)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1217)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1423)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1105)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6204)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:799)
    at android.view.Choreographer.doCallbacks(Choreographer.java:612)
    at android.view.Choreographer.doFrame(Choreographer.java:581)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:785)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5593)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Anyone have an idea what is causing this intermittent error?
 

JohnC

Expert
Licensed User
Longtime User
I am still getting this critical NR crash intermittently.

And because it's happening right on the event sub declare line (before my code starts), I can't even trap it :(

If I had to take a guess, it seems like this event is being triggered intermittently before the UI or the Activity can properly handle it, leading to a NULL pointer exception.

Meaning, there are times when my app's main activity (the activity hosting your grid) could receive the focus (Activity_Resume), and immediately switch to a different activity. And I'm thinking that when the main activity got the focus, it caused a refresh/reload cycle of your grid control on it. But before the grid view was able to go through and refresh all the items in it (the grid calls an event sub for each item), the main activity lost focus (because focus was transferred to the new activity) but the grid still tried to invoke all it's event, but it's host activity was not in a state to handle it because it didn't have the focus. But this is only a guess. But, if this is the reason, is there a way to check to make sure that when your grid is looping through the GetContent events for all the items, could it check to make sure the hosting activity has focus/control before invoking the event?

I can't introduce some artificial delay before switching to a different activity because: A) It will slow the appearance of UI to the user, B) There is no way to determine the needed delay time because it could be based on many things, like the speed of the users device or the number of items in the grid being refreshed.

*** This is pretty critical issue because I am using it in a live beta app in the play store:

https://play.google.com/store/apps/details?id=com.omnisoft.voiceit

So, it would be GREATLY appreciated if you could fix this issue as soon as possible.

Thanks!:)
 
Last edited:

JohnC

Expert
Licensed User
Longtime User
Last edited:

JohnC

Expert
Licensed User
Longtime User
Top