Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Librem5
calls
Commits
da298c06
Commit
da298c06
authored
Jul 22, 2019
by
Bob Ham
Browse files
Record calls to an SQLite database via libgom
Closes use-cases#114
parent
ebf579af
Changes
8
Hide whitespace changes
Inline
Side-by-side
debian/control
View file @
da298c06
...
...
@@ -10,6 +10,7 @@ Build-Depends:
libmm-glib-dev,
libgsound-dev,
libpeas-dev,
libgom-1.0-dev,
meson,
pkg-config,
# to run the tests
...
...
meson.build
View file @
da298c06
...
...
@@ -47,6 +47,7 @@ full_calls_plugin_libdir = join_paths(prefix, libdir, calls_name, 'plugins')
config_data
=
configuration_data
()
config_data
.
set_quoted
(
'APP_ID'
,
calls_id
)
config_data
.
set_quoted
(
'APP_DATA_NAME'
,
calls_name
)
config_data
.
set_quoted
(
'GETTEXT_PACKAGE'
,
calls_name
)
config_data
.
set_quoted
(
'LOCALEDIR'
,
full_localedir
)
config_data
.
set_quoted
(
'PLUGIN_LIBDIR'
,
full_calls_plugin_libdir
)
...
...
src/calls-application.c
View file @
da298c06
...
...
@@ -30,6 +30,7 @@
#include
"calls-new-call-box.h"
#include
"calls-encryption-indicator.h"
#include
"calls-ringer.h"
#include
"calls-record-store.h"
#include
"calls-call-window.h"
#include
"calls-main-window.h"
#include
"calls-application.h"
...
...
@@ -53,9 +54,10 @@ struct _CallsApplication
{
GtkApplication
parent_instance
;
GString
*
provider_name
;
CallsProvider
*
provider
;
CallsRinger
*
ringer
;
GString
*
provider_name
;
CallsProvider
*
provider
;
CallsRinger
*
ringer
;
CallsRecordStore
*
record_store
;
};
G_DEFINE_TYPE
(
CallsApplication
,
calls_application
,
GTK_TYPE_APPLICATION
)
...
...
@@ -241,6 +243,9 @@ activate (GApplication *application)
self
->
ringer
=
calls_ringer_new
(
self
->
provider
);
g_assert
(
self
->
ringer
!=
NULL
);
self
->
record_store
=
calls_record_store_new
(
self
->
provider
);
g_assert
(
self
->
record_store
!=
NULL
);
}
/*
...
...
src/calls-call-record.c
0 → 100644
View file @
da298c06
/*
* Copyright (C) 2018, 2019 Purism SPC
*
* This file is part of Calls.
*
* Calls is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calls is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include
"calls-call-record.h"
#include
"util.h"
#include
<glib/gi18n.h>
struct
_CallsCallRecord
{
GomResource
parent_instance
;
guint
id
;
gchar
*
target
;
gboolean
inbound
;
GDateTime
*
start
;
GDateTime
*
answered
;
GDateTime
*
end
;
gboolean
complete
;
};
G_DEFINE_TYPE
(
CallsCallRecord
,
calls_call_record
,
GOM_TYPE_RESOURCE
)
enum
{
PROP_0
,
PROP_ID
,
PROP_TARGET
,
PROP_INBOUND
,
PROP_START
,
PROP_ANSWERED
,
PROP_END
,
PROP_LAST_PROP
,
};
static
GParamSpec
*
props
[
PROP_LAST_PROP
];
static
void
get_property
(
GObject
*
object
,
guint
property_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
CallsCallRecord
*
self
=
CALLS_CALL_RECORD
(
object
);
switch
(
property_id
)
{
#define case_set(prop,type,member) \
case PROP_##prop: \
g_value_set_##type (value, self->member); \
break;
case_set
(
ID
,
uint
,
id
);
case_set
(
TARGET
,
string
,
target
);
case_set
(
INBOUND
,
boolean
,
inbound
);
case_set
(
START
,
boxed
,
start
);
case_set
(
ANSWERED
,
boxed
,
answered
);
case_set
(
END
,
boxed
,
end
);
#undef case_set
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
property_id
,
pspec
);
break
;
}
}
static
void
set_date_time
(
GDateTime
**
stamp_ptr
,
const
GValue
*
value
)
{
gpointer
new_stamp
=
g_value_get_boxed
(
value
);
g_clear_pointer
(
stamp_ptr
,
g_date_time_unref
);
if
(
new_stamp
)
{
*
stamp_ptr
=
g_date_time_ref
(
new_stamp
);
}
}
static
void
set_property
(
GObject
*
object
,
guint
property_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
CallsCallRecord
*
self
=
CALLS_CALL_RECORD
(
object
);
switch
(
property_id
)
{
case
PROP_ID
:
self
->
id
=
g_value_get_uint
(
value
);
break
;
case
PROP_TARGET
:
CALLS_SET_PTR_PROPERTY
(
self
->
target
,
g_value_dup_string
(
value
));
break
;
case
PROP_INBOUND
:
self
->
inbound
=
g_value_get_boolean
(
value
);
break
;
case
PROP_START
:
set_date_time
(
&
self
->
start
,
value
);
break
;
case
PROP_ANSWERED
:
set_date_time
(
&
self
->
answered
,
value
);
break
;
case
PROP_END
:
set_date_time
(
&
self
->
end
,
value
);
break
;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
property_id
,
pspec
);
break
;
}
}
static
void
finalize
(
GObject
*
object
)
{
GObjectClass
*
parent_class
=
g_type_class_peek
(
G_TYPE_OBJECT
);
CallsCallRecord
*
self
=
CALLS_CALL_RECORD
(
object
);
g_clear_pointer
(
&
self
->
end
,
g_date_time_unref
);
g_clear_pointer
(
&
self
->
answered
,
g_date_time_unref
);
g_clear_pointer
(
&
self
->
start
,
g_date_time_unref
);
g_free
(
self
->
target
);
parent_class
->
finalize
(
object
);
}
static
void
calls_call_record_class_init
(
CallsCallRecordClass
*
klass
)
{
GObjectClass
*
object_class
=
G_OBJECT_CLASS
(
klass
);
GomResourceClass
*
resource_class
=
GOM_RESOURCE_CLASS
(
klass
);
object_class
->
finalize
=
finalize
;
object_class
->
get_property
=
get_property
;
object_class
->
set_property
=
set_property
;
gom_resource_class_set_table
(
resource_class
,
"calls"
);
#define install(NAME) \
g_object_class_install_property \
(object_class, PROP_##NAME, props[PROP_##NAME])
/*
* NB: ANY ADDITIONS TO THIS LIST REQUIRE AN INCREASE IN
* RECORD_STORE_VERSION IN calls-record-store.c AND THE USE OF
*
* gom_resource_class_set_property_new_in_version
* (resource_class, "property", 2);
*
* HERE.
*/
props
[
PROP_ID
]
=
g_param_spec_uint
(
"id"
,
_
(
"ID"
),
_
(
"The row ID"
),
0
,
G_MAXUINT
,
0
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
);
install
(
ID
);
gom_resource_class_set_primary_key
(
resource_class
,
"id"
);
props
[
PROP_TARGET
]
=
g_param_spec_string
(
"target"
,
_
(
"Target"
),
_
(
"The PTSN phone number or other address of the call"
),
NULL
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
);
install
(
TARGET
);
props
[
PROP_INBOUND
]
=
g_param_spec_boolean
(
"inbound"
,
_
(
"Inbound"
),
_
(
"Whether the call was an inbound call"
),
FALSE
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
);
install
(
INBOUND
);
props
[
PROP_START
]
=
g_param_spec_boxed
(
"start"
,
_
(
"Start"
),
_
(
"Time stamp of the start of the call"
),
G_TYPE_DATE_TIME
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
);
install
(
START
);
props
[
PROP_ANSWERED
]
=
g_param_spec_boxed
(
"answered"
,
_
(
"Answered"
),
_
(
"Time stamp of when the call was answered"
),
G_TYPE_DATE_TIME
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
);
install
(
ANSWERED
);
props
[
PROP_END
]
=
g_param_spec_boxed
(
"end"
,
_
(
"End"
),
_
(
"Time stamp of the end of the call"
),
G_TYPE_DATE_TIME
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
);
install
(
END
);
/*
* NB: ANY ADDITIONS TO THIS LIST REQUIRE AN INCREASE IN
* RECORD_STORE_VERSION IN calls-record-store.c AND THE USE OF
*
* gom_resource_class_set_property_new_in_version
* (resource_class, "property", 2);
*
* HERE.
*/
#undef install
}
static
void
calls_call_record_init
(
CallsCallRecord
*
self
)
{
}
src/calls-call-record.h
0 → 100644
View file @
da298c06
/*
* Copyright (C) 2018, 2019 Purism SPC
*
* This file is part of Calls.
*
* Calls is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calls is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef CALLS_CALL_RECORD_H__
#define CALLS_CALL_RECORD_H__
#include
<gom/gom.h>
G_BEGIN_DECLS
#define CALLS_TYPE_CALL_RECORD (calls_call_record_get_type ())
G_DECLARE_FINAL_TYPE
(
CallsCallRecord
,
calls_call_record
,
CALLS
,
CALL_RECORD
,
GomResource
);
G_END_DECLS
#endif
/* CALLS_CALL_RECORD_H__ */
src/calls-record-store.c
0 → 100644
View file @
da298c06
/*
* Copyright (C) 2018, 2019 Purism SPC
*
* This file is part of Calls.
*
* Calls is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calls is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include
"calls-record-store.h"
#include
"calls-call-record.h"
#include
"calls-enumerate.h"
#include
"calls-call.h"
#include
"config.h"
#include
<gom/gom.h>
#include
<glib/gi18n.h>
#include
<errno.h>
#define RECORD_STORE_FILENAME "records.db"
#define RECORD_STORE_VERSION 1
typedef
enum
{
STARTED
,
ANSWERED
,
ENDED
}
CallsCallRecordState
;
static
CallsCallRecordState
state_to_record_state
(
CallsCallState
call_state
)
{
switch
(
call_state
)
{
case
CALLS_CALL_STATE_DIALING
:
case
CALLS_CALL_STATE_ALERTING
:
case
CALLS_CALL_STATE_INCOMING
:
case
CALLS_CALL_STATE_WAITING
:
return
STARTED
;
case
CALLS_CALL_STATE_ACTIVE
:
case
CALLS_CALL_STATE_HELD
:
return
ANSWERED
;
case
CALLS_CALL_STATE_DISCONNECTED
:
return
ENDED
;
}
g_assert_not_reached
();
}
struct
_CallsRecordStore
{
GtkApplicationWindow
parent_instance
;
gchar
*
filename
;
CallsProvider
*
provider
;
GomAdapter
*
adapter
;
GomRepository
*
repository
;
};
G_DEFINE_TYPE
(
CallsRecordStore
,
calls_record_store
,
G_TYPE_OBJECT
);
enum
{
PROP_0
,
PROP_PROVIDER
,
PROP_LAST_PROP
,
};
static
GParamSpec
*
props
[
PROP_LAST_PROP
];
static
void
set_up_repo_migrate_cb
(
GomRepository
*
repo
,
GAsyncResult
*
res
,
CallsRecordStore
*
self
)
{
GError
*
error
=
NULL
;
gboolean
ok
;
ok
=
gom_repository_automatic_migrate_finish
(
repo
,
res
,
&
error
);
if
(
!
ok
)
{
if
(
error
)
{
g_warning
(
"Error migrating call record database `%s': %s"
,
self
->
filename
,
error
->
message
);
g_error_free
(
error
);
}
else
{
g_warning
(
"Unknown error migrating call record database `%s'"
,
self
->
filename
);
}
g_clear_object
(
&
self
->
repository
);
g_clear_object
(
&
self
->
adapter
);
}
else
{
g_debug
(
"Successfully migrated call record database `%s'"
,
self
->
filename
);
}
}
static
void
set_up_repo
(
CallsRecordStore
*
self
)
{
GomRepository
*
repo
;
GList
*
types
=
NULL
;
if
(
self
->
repository
)
{
g_warning
(
"Opened call record database `%s'"
" while repository exists"
,
self
->
filename
);
return
;
}
repo
=
gom_repository_new
(
self
->
adapter
);
g_debug
(
"Attempting migration of call"
" record database `%s'"
,
self
->
filename
);
types
=
g_list_append
(
types
,
(
gpointer
)
CALLS_TYPE_CALL_RECORD
);
gom_repository_automatic_migrate_async
(
repo
,
RECORD_STORE_VERSION
,
types
,
(
GAsyncReadyCallback
)
set_up_repo_migrate_cb
,
self
);
self
->
repository
=
repo
;
}
static
void
close_adapter
(
CallsRecordStore
*
self
)
{
GError
*
error
=
NULL
;
gboolean
ok
;
if
(
!
self
->
adapter
)
{
return
;
}
ok
=
gom_adapter_close_sync
(
self
->
adapter
,
&
error
);
if
(
!
ok
)
{
if
(
error
)
{
g_warning
(
"Error closing call record database `%s': %s"
,
self
->
filename
,
error
->
message
);
g_error_free
(
error
);
}
else
{
g_warning
(
"Unknown error closing call record database `%s'"
,
self
->
filename
);
}
}
g_clear_object
(
&
self
->
adapter
);
}
static
void
open_repo_adapter_open_cb
(
GomAdapter
*
adapter
,
GAsyncResult
*
res
,
CallsRecordStore
*
self
)
{
GError
*
error
=
NULL
;
gboolean
ok
;
ok
=
gom_adapter_open_finish
(
adapter
,
res
,
&
error
);
if
(
!
ok
)
{
if
(
error
)
{
g_warning
(
"Error opening call record database `%s': %s"
,
self
->
filename
,
error
->
message
);
g_error_free
(
error
);
}
else
{
g_warning
(
"Unknown error opening call record database `%s'"
,
self
->
filename
);
}
close_adapter
(
self
);
}
else
{
g_debug
(
"Successfully opened call record database `%s'"
,
self
->
filename
);
set_up_repo
(
self
);
}
}
static
void
open_repo
(
CallsRecordStore
*
self
)
{
gchar
*
dir
;
gint
err
;
gchar
*
uri
;
if
(
self
->
adapter
)
{
return
;
}
dir
=
g_path_get_dirname
(
self
->
filename
);
err
=
g_mkdir_with_parents
(
dir
,
0755
);
if
(
err
)
{
g_warning
(
"Could not create Calls data directory `%s': %s"
,
dir
,
g_strerror
(
errno
));
}
g_free
(
dir
);
uri
=
g_strdup_printf
(
"file:%s"
,
self
->
filename
);
g_debug
(
"Opening call record database using URI `%s'"
,
uri
);
self
->
adapter
=
gom_adapter_new
();
gom_adapter_open_async
(
self
->
adapter
,
uri
,
(
GAsyncReadyCallback
)
open_repo_adapter_open_cb
,
self
);
g_free
(
uri
);
}
static
void
record_call_save_cb
(
GomResource
*
resource
,
GAsyncResult
*
res
,
CallsCall
*
call
)
{
GObject
*
const
call_obj
=
G_OBJECT
(
call
);
GError
*
error
=
NULL
;
gboolean
ok
;
ok
=
gom_resource_save_finish
(
resource
,
res
,
&
error
);
if
(
!
ok
)
{
if
(
error
)
{
g_warning
(
"Error saving call record to database: %s"
,
error
->
message
);
g_error_free
(
error
);
}
else
{
g_warning
(
"Unknown error saving call record to database"
);
}
g_object_set_data
(
call_obj
,
"calls-call-record"
,
NULL
);
}
else
{
g_debug
(
"Successfully saved new call record to database"
);
g_object_set_data
(
call_obj
,
"calls-call-start"
,
NULL
);
}
}
static
void
record_call
(
CallsRecordStore
*
self
,
CallsCall
*
call
)