B4A Library Phone library update - v1.34

Status
Not open for further replies.
Edit: V1.34 is available.
This version adds two new events: ConnectivityChanged and TextToSpeechFinish. This version also adds a new object named SmsInterceptor that handles new incoming Sms messages.

A simple example that prints the events to the LogCat:
Main activity
B4X:
Sub Process_Globals
    Dim TTS As TTS
End Sub
Sub Globals

End Sub

Sub Activity_Create(FirstTime As Boolean)
    StartService(S1)
    TTS.Initialize("tts")
End Sub
Sub TTS_Ready (Success As Boolean)
    If Success Then TTS.Speak("hello world", False)
End Sub
S1 service
B4X:
'Service module
Sub Process_Globals
    Dim PE As PhoneEvents
    Dim SI As SMSInterceptor
End Sub
Sub Service_Create
    PE.Initialize("PE")
    SI.Initialize("SI")
End Sub

Sub Service_Start

End Sub
Sub SI_MessageReceived (From As String, Body As String)
    Log("MessageReceived: From = " & From & ", Body = " & Body)
End Sub
Sub PE_TextToSpeechFinish (Intent As Intent)
    Log("TextToSpeechFinish")
End Sub
Sub PE_ConnectivityChanged (NetworkType As String, State As String, Intent As Intent)
    Log("ConnectivityChanged: " & NetworkType & ", state = " & State)
    Log(Intent.ExtrasToString)
End Sub
Sub PE_AirplaneModeChanged (State As Boolean, Intent As Intent)
    Log("AirplaneModeChanged: "& state)
    Log(Intent.ExtrasToString)
End Sub
Sub PE_BatteryChanged (Level As Int, Scale As Int, Plugged As Boolean, Intent As Intent)
    Log("BatteryChanged: Level = " & level & ", Scale = " & scale & ", Plugged = " & Plugged)
End Sub
Sub PE_CameraButtonPressed (Intent As Intent)
    Log("CameraButtonPressed")
    Log(Intent.ExtrasToString)
End Sub
Sub PE_PackageRemoved (Package As String, Intent As Intent)
    Log("PackageRemoved: " & Package)
    Log(Intent.ExtrasToString)
End Sub
Sub PE_PackageAdded (Package As String, Intent As Intent)
    Log("PackageAdded: " & Package)
    Log(intent.ExtrasToString)
End Sub
Sub PE_ScreenOff (Intent As Intent)
    Log("ScreenOff")
End Sub
Sub PE_ScreenOn (Intent As Intent)
    Log("ScreenOn")
End Sub
Sub PE_Shutdown (Intent As Intent)
    Log("Shutdown")
End Sub
Sub PE_UserPresent (Intent As Intent)
    Log("UserPresent")
End Sub
Sub PE_PhoneStateChanged (State As String, IncomingNumber As String, Intent As Intent)
    Log("PhoneStateChanged, State = " & State & ", IncomingNumber = " & IncomingNumber)
    Log(Intent.ExtrasToString)
End Sub
Sub Service_Destroy
    
End Sub
Installation instructions: Unzip the attached file and copy both files to the internal libraries folder. The default is: C:\Program Files\Anywhere Software\Basic4android\Libraries
 

Attachments

  • Phone.zip
    50.5 KB · Views: 2,010

corwin42

Expert
Licensed User
Longtime User
Wow this is a great feature.

Is there a possibility to receive an event if Wifi gets connected?
I want to write a service that begins its work only when there is a Wifi connection.
 

kkolle

Member
Licensed User
Longtime User
Example?!

Hi Erel!

Great work!

A small example with the phoneevents in a service would be nice .... :sign0085:
 

Cor

Active Member
Licensed User
Longtime User
I want to put recevied message in a label lblBericht which is located
from layout main from mode main

how to solve this?

I want to keep the service in the same service module.

Or can I move the Sub SI_MessageReceived (From As String, Body As String)
to a different module.


B4X:
Sub SI_MessageReceived (From As String, Body As String)
    Log("MessageReceived: From = " & From & ", Body = " & Body)
    lblBericht.text=From   <-- cannot access this from main
End Sub
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
First you should understand that it is possible for this event to be raised while your activity is paused.
The best solution is to store the text in a process global object.
And also use CallSub(Main, "AfterMessageReceived") to call a sub in Main named AfterMessageReceived. In this sub you should get the value from the service process global object.
You should also check this variable in Activity_Resume to make sure you didn't miss any event while being paused.
 

ZJP

Active Member
Licensed User
Longtime User
Hi,

Is there a way to know the running applications? A sort of "IsRunning list", and a event when a application is launching?

JP
 

andreas

Member
Licensed User
Longtime User
SMS text length limitation

Hi,

When I'm trying to send sms messages with body text more than 160 characters, the program returns a "java.lang.NullPointerException".
How can i solve this problem ?


B4X:
Sub SendMessage(Phone as string, LongText as string)
 Dim Ph As PhoneSms 
 ph.Send(Phone, LongText)
End Sub
 

moster67

Expert
Licensed User
Longtime User

andreas

Member
Licensed User
Longtime User

moster67

Expert
Licensed User
Longtime User
Sorry Andreas but I am afraid I can't help you with that. I simply referred you to the other thread since your issue seemed similar.

moster67, thank you for your response.
I had seen this thread before, but it can't solve the problem. (maybe i'm doing something wrong)
When i split the long message to multiple smaller messages (of 159 or 160 chars each) and sending them, they are recieved as seperated messages in phones that supports long text (HTC P3300 and HTC Desire)
 

agraham

Expert
Licensed User
Longtime User
To be received as a single long message multiple messages use the beginning 48 bits of each message that makes up the long message as a header that contains the information the receiving phone needs to identify that a single message is part of a longer message. This header is part of the payload and so reduces the number of characters that the message can carry to 153 characters. The sending device needs to add the required header to each sub-message and although it might be theoretically possible to compose the first seven characters of each message to represent the required header it is not really practical to do so.
 

TomK

Member
Licensed User
Longtime User
Contacts in Phone Library

Hi guys,

I just want to say Erel, you have a wonderful product here and sincerely you have my thanks for making it!!


That said, I would like to inquire about the contacts as they exist in the library right now. I'm trying to resolve Phone contacts with GMail contacts and some of the rudimentary information contained in both (ie., name, address, city, state, zip, country, phone number, email) and I have run into 2 stumbling blocks and perhaps I'm simply missing something.

Issue #1 - It seems only those contacts which actually reside as a gmail contact get acted upon (in this case I was simply sending all info on all contacts to the Log)

Issue # 2 - It seems not all information that could be possible for any given contact is available; in my case I was specifically looking for the Postal address.


Any guidance on this would be greatly appreciated, although I suspect it may be in future updates in which case would my recourse be to get the functionality I need from firing up Eclipse in the meantime?


Thank you in advance!



Tom Kwasnik
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Thank you for your feedback :)

Issue #1 - It seems only those contacts which actually reside as a gmail contact get acted upon (in this case I was simply sending all info on all contacts to the Log)
Which contacts are missing? Of a specific source?

Issue # 2 - It seems not all information that could be possible for any given contact is available; in my case I was specifically looking for the Postal address.
The Postal field is currently not exposed.
I will add it to the features list.
Here is the code for the Contacts. Adding a field shouldn't be difficult.


B4X:
package anywheresoftware.b4a.phone;

import java.util.HashMap;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.Contacts;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.IOnActivityResult;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.Permissions;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.keywords.Common;
import anywheresoftware.b4a.objects.collections.List;
import anywheresoftware.b4a.objects.collections.Map;
import anywheresoftware.b4a.objects.drawable.CanvasWrapper.BitmapWrapper;
import anywheresoftware.b4a.objects.streams.File.InputStreamWrapper;

/**
 * Contacts object allows you to access the device stored contacts.
 *The following code finds all contacts named John (actually it will find all contacts which their name contains the string "john"),
 *and print its fields to the LogCat. It will also fetch the contact photo if it exists.
 *Example:<code>
 *Dim Contacts1 As Contacts
 *Dim listOfContacts As List
 *listOfContacts = Contacts1.FindByName("John", False)
 *For i = 0 To listOfContacts.Size - 1
 *    Dim Contact As Contact
 *    Contact = listOfContacts.Get(i)
 *    Log(Contact) 'will print the fields to the LogCat
 *    Dim photo As Bitmap
 *    photo = Contact.GetPhoto
 *    If photo <> Null Then Activity.SetBackgroundImage(photo)
 *    Dim emails As Map
 *    emails = Contact.GetEmails
 *    If emails.Size > 0 Then Log("Email addresses: " & emails)
 *    Dim phones As Map
 *    phones = Contact.GetPhones
 *    If phones.Size > 0 Then Log("Phone numbers: " & phones)
 *Next</code>
 */
@ShortName("Contacts")
@Permissions(values={"android.permission.READ_CONTACTS"})
public class ContactsWrapper {
    private static final String[] people_projection = {Contacts.People.TIMES_CONTACTED,
        Contacts.Phones.NUMBER, Contacts.People.LAST_TIME_CONTACTED,
        Contacts.People.DISPLAY_NAME, Contacts.People.NAME, Contacts.People.NOTES, Contacts.People.STARRED, BaseColumns._ID};
    
    /**
     * Returns a List of Contact objects with all the contacts. This list can be very large.
     */
    public List GetAll() {
        return getAllContacts(null, null);
    }
    /**
     * Returns a List of Contact objects with all contacts matching the given name.
     *Name - The name to search for.
     *Exact - If True then only contacts with the exact name value (case sensitive) will return
     *, otherwise all contacts names that include the Name string will return (case insensitive).
     */
    public List FindByName(String Name, boolean Exact) {
        if (!Exact)
            return getAllContacts(Contacts.People.NAME + " LIKE ?", new String[] {"%" + Name + "%"});
        else
            return getAllContacts(Contacts.People.NAME + " = ?", new String[] {Name});
    }
    /**
     * Returns a List of Contact objects with all contacts matching the given email.
     *Email - The email to search for.
     *Exact - If True then only contacts with the exact email address (case sensitive) will return
     *, otherwise all contacts email addresses that include the Email string will return (case insensitive).
     */
    public List FindByMail(String Email, boolean Exact) {
        ContentResolver cr = BA.applicationContext.getContentResolver();
        String sel, args;
        if (!Exact) {
            sel = " LIKE ?";
            args = "%" + Email + "%";
        }
        else {
            sel = " = ?";
            args = Email;
        }
        Cursor crsr = cr.query(Contacts.ContactMethods.CONTENT_EMAIL_URI, new String[] {Contacts.ContactMethods.PERSON_ID,
                Contacts.ContactMethods.DATA}, Contacts.ContactMethods.DATA + sel, new String[] {args}, null);
        StringBuilder sb = new StringBuilder();
        while (crsr.moveToNext()) {
            for (int i = 0;i < crsr.getColumnCount();i++) {
                sb.append(crsr.getString(0)).append(",");
            }
        }
        int count = crsr.getCount();
        crsr.close();
        if (count == 0) {
            List l = new List();
            l.Initialize();
            return l;
        }
        sb.setLength(sb.length() - 1);
        String selection = BaseColumns._ID +  " IN (" + sb.toString() + ")";
        return getAllContacts(selection, null);
    }
    /**
     * Returns the Contact with the specified Id.
     * Returns Null if no matching contact found.
     */
    public Contact GetById(int Id) {
        List l = getAllContacts(BaseColumns._ID + " = ?", new String[] {String.valueOf(Id)});
        if (l.getSize() == 0)
            return null;
        else
            return (Contact) l.Get(0);
    }
    private List getAllContacts(String selection, String[] args) {

        ContentResolver cr = BA.applicationContext.getContentResolver();
        Cursor crsr = cr.query(Contacts.People.CONTENT_URI, people_projection, selection, args, null);
        List l = new List();
        l.Initialize();
        HashMap<String, Integer> m = new HashMap<String, Integer>();
        for (int col = 0;col < crsr.getColumnCount();col++) {
            m.put(crsr.getColumnName(col), col);
        }
        
        while (crsr.moveToNext()) {
            Contact contact = new Contact(
                    crsr.getString(m.get(Contacts.People.DISPLAY_NAME)),
                    crsr.getString(m.get(Contacts.Phones.NUMBER)),
                    crsr.getInt(m.get(Contacts.People.STARRED)) > 0,
                    crsr.getInt(m.get(BaseColumns._ID)),
                    crsr.getString(m.get(Contacts.People.NOTES)),
                    crsr.getInt(m.get(Contacts.People.TIMES_CONTACTED)),
                    crsr.getLong(m.get(Contacts.People.LAST_TIME_CONTACTED)),
                    crsr.getString(m.get(Contacts.People.NAME)));
            l.Add(contact);
        }
        crsr.close();
        return l;
    }

    /**
     * Represents a single contact.
     *The Contacts object should be used to get lists of Contact objects.
     *EMAIL_x constants are the possible email types.
     *PHONE_x constants are the possible phone types.
     */
    @ShortName("Contact")
    public static class Contact {
        public static final int EMAIL_CUSTOM = 0;
        public static final int EMAIL_HOME = 1;
        public static final int EMAIL_WORK = 2;
        public static final int EMAIL_OTHER = 3;
        
        public static final int PHONE_CUSTOM = 0;
        public static final int PHONE_HOME = 1;
        public static final int PHONE_MOBILE = 2;
        public static final int PHONE_WORK = 3;
        public static final int PHONE_FAX_WORK = 4;
        public static final int PHONE_FAX_HOME = 5;
        public static final int PHONE_PAGER = 6;
        public static final int PHONE_OTHER = 7;
        /**
         * The displayed name. Equals to the Name if the Name is not empty, otherwise equals to the contacts first email address.
         */
        public String DisplayName;
        /**
         * Primary phone number.
         */
        public String PhoneNumber = "";
        /**
         * Whether this contact is a "favorite" contact.
         */
        public boolean Starred;
        /**
         * Internal Id.
         */
        public int Id = -1;
        public String Notes;
        /**
         * Number of times that this contact was contacted.
         */
        public int TimesContacted;
        /**
         * Last time that this contact was contacted. Value is a ticks value.
         */
        public long LastTimeContacted;
        /**
         * Contact name.
         */
        public String Name;
        public Contact() {}
        Contact(String displayName, String phoneNumber, boolean starred,
                int id,  String notes,
                int timesContacted,
                long lastTimeContacted, String name) {
            DisplayName = displayName == null ? "" : displayName;
            PhoneNumber = phoneNumber == null ? "" : phoneNumber; 
            Starred = starred;
            Id = id;
            Notes = notes == null ? "" : notes;
            TimesContacted = timesContacted;
            LastTimeContacted = lastTimeContacted;
            Name = name == null ? "" : name;
        }
        /**
         * Returns the contact photo or Null if there is no attached photo.
         *This call executes an additional query.
         */
        public BitmapWrapper GetPhoto() {
            if (Id == -1)
                throw new RuntimeException("Contact object should be set by calling one of the Contacts methods.");
            Uri u = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.People.CONTENT_URI, Id), 
                    Contacts.Photos.CONTENT_DIRECTORY);
            Cursor crsr = BA.applicationContext.getContentResolver().query(u, new String[] {Contacts.Photos.DATA}, null, null, null);
            BitmapWrapper bw = null;
            if (crsr.moveToNext()) {
                byte[] b = crsr.getBlob(0);
                if (b != null) {
                    InputStreamWrapper isw = new InputStreamWrapper();
                    isw.InitializeFromBytesArray(b, 0, b.length);
                    bw = new BitmapWrapper();
                    bw.Initialize2(isw.getObject());
                }
            }
            crsr.close();
            return bw;
        }
        /**
         * Returns a Map with the contacts email addresses as keys and the email types as values.
         *This call executes an additional query.
         */
        public Map GetEmails() {
            if (Id == -1)
                throw new RuntimeException("Contact object should be set by calling one of the Contacts methods.");
            Uri u = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.People.CONTENT_URI, Id), 
                    Contacts.People.ContactMethods.CONTENT_DIRECTORY);
            Cursor crsr = BA.applicationContext.getContentResolver().query(u, new String[] {Contacts.ContactMethods.DATA,Contacts.ContactMethods.TYPE,
                    Contacts.ContactMethods.KIND}, 
                    Contacts.ContactMethods.KIND + " = " + Contacts.KIND_EMAIL, null, null);
            Map m = new Map(); m.Initialize();
            while (crsr.moveToNext()) {
                m.Put(crsr.getString(0), crsr.getInt(1));
            }
            crsr.close();
            return m;
        }
        /**
         * Returns a Map with all the contacts phone numbers as keys and the phone types as values.
         *This call executes an additional query.
         */
        public Map GetPhones() {
            if (Id == -1)
                throw new RuntimeException("Contact object should be set by calling one of the Contacts methods.");
            Uri u = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.People.CONTENT_URI, Id), 
                    Contacts.People.Phones.CONTENT_DIRECTORY);
            Cursor crsr = BA.applicationContext.getContentResolver().query(u, new String[] {Contacts.PhonesColumns.NUMBER, Contacts.PhonesColumns.TYPE}, 
                    null, null, null);
            Map m = new Map(); m.Initialize();
            while (crsr.moveToNext()) {
                m.Put(crsr.getString(0), crsr.getInt(1));
            }
            crsr.close();
            return m;
        }
        @Override
        @Hide
        public String toString() {
            return "DisplayName=" + DisplayName + ", PhoneNumber=" + PhoneNumber + 
            ", Starred=" + Starred + ", Id=" + Id + ", Notes=" + Notes + ", TimesContacted=" + 
            TimesContacted + ", LastTimeContacted=" + LastTimeContacted + ", Name=" + Name;
        }
    }
}
 
Status
Not open for further replies.
Top