Merge remote-tracking branch 'origin' into dev_codec_downloader
This commit is contained in:
commit
285bdbcadd
43 changed files with 1564 additions and 164 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -58,9 +58,6 @@
|
|||
[submodule "submodules/externals/libupnp"]
|
||||
path = submodules/externals/libupnp
|
||||
url = git://git.linphone.org/libupnp.git
|
||||
[submodule "submodules/externals/cunit"]
|
||||
path = submodules/externals/cunit
|
||||
url = git://git.linphone.org/cunit.git
|
||||
[submodule "submodules/externals/axmlrpc"]
|
||||
path = submodules/externals/axmlrpc
|
||||
url = git://git.linphone.org/axmlrpc.git
|
||||
|
@ -104,3 +101,6 @@
|
|||
[submodule "submodules/belcard"]
|
||||
path = submodules/belcard
|
||||
url = git://git.linphone.org/belcard.git
|
||||
[submodule "submodules/bcunit"]
|
||||
path = submodules/bcunit
|
||||
url = git://git.linphone.org/bcunit.git
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
|
||||
<!-- Needed for in-app purchase -->
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
|
||||
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true"/>
|
||||
|
||||
|
@ -133,6 +135,14 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.linphone.purchase.InAppPurchaseActivity"
|
||||
android:theme="@style/NoTitle"
|
||||
android:screenOrientation="nosensor">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="org.linphone.assistant.RemoteProvisioningLoginActivity"
|
||||
android:theme="@style/NoTitle"
|
||||
android:screenOrientation="nosensor">
|
||||
|
|
|
@ -84,7 +84,7 @@ class AndroidPreparator(prepare.Preparator):
|
|||
def __init__(self, targets=android_targets):
|
||||
prepare.Preparator.__init__(self, targets)
|
||||
self.min_supported_ndk = 10
|
||||
self.max_supported_ndk = 11
|
||||
self.max_supported_ndk = 12
|
||||
self.unsupported_ndk_version = None
|
||||
self.release_with_debug_info = True
|
||||
self.veryclean = True
|
||||
|
@ -138,8 +138,8 @@ class AndroidPreparator(prepare.Preparator):
|
|||
python_config_files.append(os.path.join(root, filename))
|
||||
if len(python_config_files) > 0:
|
||||
version = open(python_config_files[0]).readlines()[0]
|
||||
res = re.match('^.*/aosp-ndk-r(\d+).*$', version)
|
||||
version = int(res.group(1))
|
||||
res = re.match('^.*/(aosp-)?ndk-r(\d+).*$', version)
|
||||
version = int(res.group(2))
|
||||
retval = False
|
||||
else:
|
||||
error("Could not get Android NDK version!")
|
||||
|
|
|
@ -241,15 +241,32 @@
|
|||
android:layout_weight="0.5"
|
||||
android:padding="12dp"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chat"
|
||||
android:src="@drawable/footer_chat"
|
||||
android:background="@color/colorC"
|
||||
android:contentDescription="@string/content_description_chat_button"
|
||||
<RelativeLayout
|
||||
android:background="@drawable/footer_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="15dp"/>
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chat"
|
||||
android:src="@drawable/footer_chat"
|
||||
android:background="@color/colorC"
|
||||
android:contentDescription="@string/content_description_chat_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="15dp"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/missed_chats"
|
||||
android:background="@drawable/history_chat_indicator"
|
||||
style="@style/font18"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_margin="15dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"/>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
|
|
@ -241,15 +241,32 @@
|
|||
android:padding="12dp"
|
||||
android:contentDescription="@string/content_description_hang_up"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chat"
|
||||
android:src="@drawable/footer_chat"
|
||||
android:background="@color/colorC"
|
||||
<RelativeLayout
|
||||
android:background="@drawable/footer_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="15dp"
|
||||
android:contentDescription="@string/content_description_chat_button"/>
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chat"
|
||||
android:src="@drawable/footer_chat"
|
||||
android:background="@color/colorC"
|
||||
android:contentDescription="@string/content_description_chat_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="15dp"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/missed_chats"
|
||||
android:background="@drawable/history_chat_indicator"
|
||||
style="@style/font18"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_margin="15dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"/>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
|
|
@ -68,11 +68,18 @@
|
|||
android:layout_height="40dp"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/forgot_password"
|
||||
android:layout_gravity="right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/assistant_display_name_optional"
|
||||
style="@style/font13"
|
||||
android:textAllCaps="true"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingTop="15dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
|
|
|
@ -294,16 +294,33 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.4"
|
||||
android:padding="12dp"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chat"
|
||||
android:src="@drawable/footer_chat"
|
||||
android:background="@color/colorC"
|
||||
android:contentDescription="@string/content_description_chat_button"
|
||||
|
||||
<RelativeLayout
|
||||
android:background="@drawable/footer_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.8"
|
||||
android:padding="15dp"/>
|
||||
android:layout_weight="0.8">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chat"
|
||||
android:src="@drawable/footer_chat"
|
||||
android:background="@color/colorC"
|
||||
android:contentDescription="@string/content_description_chat_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="15dp"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/missed_chats"
|
||||
android:background="@drawable/history_chat_indicator"
|
||||
style="@style/font18"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_margin="15dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"/>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -95,6 +95,19 @@
|
|||
android:scaleType="centerInside"
|
||||
android:src="@drawable/led_connected" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/friendLinphone"
|
||||
android:visibility="gone"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginRight="20dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/linphone_user" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
19
res/layout/in_app_purchasable.xml
Normal file
19
res/layout/in_app_purchasable.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/inapp_button"
|
||||
android:text="@string/assistant_create_account"
|
||||
android:background="@drawable/assistant_button"
|
||||
style="@style/font8"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"/>
|
||||
|
||||
</LinearLayout>
|
110
res/layout/in_app_store.xml
Normal file
110
res/layout/in_app_store.xml
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/colorH"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/topbar"
|
||||
android:background="@color/colorF"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp">
|
||||
|
||||
<TextView
|
||||
android:text="@string/assistant"
|
||||
style="@style/font1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="30dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username_label"
|
||||
android:text="@string/username"
|
||||
style="@style/font13"
|
||||
android:textAllCaps="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:background="@drawable/resizable_textfield"
|
||||
android:textColor="@color/colorB"
|
||||
android:contentDescription="@string/content_description_username_field"
|
||||
android:inputType="text|textEmailAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username_error"
|
||||
android:text="@string/error"
|
||||
style="@style/font20"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/email_label"
|
||||
android:text="@string/email"
|
||||
style="@style/font13"
|
||||
android:textAllCaps="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/email"
|
||||
android:background="@drawable/resizable_textfield"
|
||||
android:textColor="@color/colorB"
|
||||
android:inputType="text|textEmailAddress"
|
||||
android:contentDescription="@string/content_description_email_field"
|
||||
android:textCursorDrawable="@null"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:singleLine="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/purchasable_items"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="40dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -7,6 +7,8 @@ contact="Linphone Android" <sip:linphone.android@unknown-host>
|
|||
use_info=0
|
||||
use_ipv6=0
|
||||
keepalive_period=30000
|
||||
rls_uri=sip:rls@sip1.linphone.org
|
||||
use_rls_presence=1
|
||||
|
||||
[video]
|
||||
size=qvga
|
||||
|
|
|
@ -31,4 +31,8 @@ dtmf_player_amp=0.1
|
|||
ec_calibrator_cool_tones=1
|
||||
|
||||
[misc]
|
||||
max_calls=10
|
||||
max_calls=10
|
||||
|
||||
[in-app-purchase]
|
||||
server_url=https://www.linphone.org/xmlrpc.php
|
||||
purchasable_items_ids=test_account_subscription
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- New settings -->
|
||||
<string name="default_domain">sip.linphone.org</string>
|
||||
<string name="default_stun">stun.linphone.org</string>
|
||||
|
@ -13,6 +14,7 @@
|
|||
<bool name="show_statusbar_only_on_dialer">false</bool>
|
||||
<bool name="replace_assistant_with_old_interface">false</bool>
|
||||
<bool name="force_use_of_linphone_friends">false</bool>
|
||||
<bool name="in_app_purchase_in_settings">false</bool>
|
||||
|
||||
<string name="wizard_url">https://www.linphone.org/wizard.php</string>
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
<string name="pref_sipaccounts_key">pref_sipaccounts_key</string>
|
||||
<string name="setup_key">setup_key</string>
|
||||
<string name="pref_add_account_key">pref_add_account_key</string>
|
||||
<string name="pref_in_app_store_key">pref_in_app_store_key</string>
|
||||
<string name="pref_video_key">pref_video_key</string>
|
||||
<string name="pref_video_codecs_key">pref_video_codecs_key</string>
|
||||
|
||||
|
@ -71,6 +72,7 @@
|
|||
<string name="pref_bandwidth_limit_key">pref_bandwidth_limit_key</string>
|
||||
<string name="pref_animation_enable_key">pref_animation_enable_key</string>
|
||||
<string name="pref_escape_plus_key">pref_escape_plus_key</string>
|
||||
<string name="pref_friendlist_subscribe_key">pref_friendlist_subscribe_key</string>
|
||||
<string name="pref_echo_cancellation_key">pref_echo_cancellation_key</string>
|
||||
<string name="pref_autostart_key">pref_autostart_key</string>
|
||||
<string name="pref_enable_outbound_proxy_key">Outbound proxy</string>
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
<string name="wizard_passwords_unmatched">Passwords do not match.</string>
|
||||
<string name="setup_confirm_username">Your username will be %s.\r\n\r\nIt may differ from your input to match requirements.\r\nDo you accept?</string>
|
||||
<string name="first_launch_no_login_password">Please enter your login and password</string>
|
||||
<string name="forgot_password">Forgot password ?</string>
|
||||
|
||||
<!-- Dailer -->
|
||||
<string name="address_bar_hint">Enter a number or an address</string>
|
||||
|
@ -206,6 +207,7 @@
|
|||
<string name="pref_avpf">AVPF</string>
|
||||
<string name="pref_avpf_rr_interval"> AVPF regular RTCP interval in seconds (between 1 and 5)</string>
|
||||
<string name="pref_escape_plus">Replace + by 00</string>
|
||||
<string name="pref_friendlist_subscribe">Friendlist subscribe</string>
|
||||
<string name="pref_auth_userid">Auth userid</string>
|
||||
<string name="pref_help_auth_userid">Enter authentication userid (optional)</string>
|
||||
<string name="pref_display_name">Display name</string>
|
||||
|
@ -222,6 +224,7 @@
|
|||
<string name="pref_sipaccounts">SIP Accounts</string>
|
||||
<string name="default_account_flag">Default account</string>
|
||||
<string name="pref_add_account">Add account</string>
|
||||
<string name="pref_in_app_store">In-app Store</string>
|
||||
<string name="pref_tunnel">Tunnel</string>
|
||||
<string name="pref_tunnel_host">Hostname</string>
|
||||
<string name="pref_tunnel_port">Port</string>
|
||||
|
|
|
@ -72,6 +72,10 @@
|
|||
<CheckBoxPreference
|
||||
android:title="@string/pref_escape_plus"
|
||||
android:key="@string/pref_escape_plus_key"/>
|
||||
|
||||
<CheckBoxPreference
|
||||
android:title="@string/pref_friendlist_subscribe"
|
||||
android:key="@string/pref_friendlist_subscribe_key"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
|
|
@ -4,11 +4,15 @@
|
|||
<PreferenceCategory
|
||||
android:title="@string/pref_sipaccounts"
|
||||
android:key="@string/pref_sipaccounts_key"/>
|
||||
|
||||
|
||||
<Preference
|
||||
android:title="@string/pref_add_account"
|
||||
android:key="@string/pref_add_account_key"/>
|
||||
|
||||
<Preference
|
||||
android:title="@string/pref_in_app_store"
|
||||
android:key="@string/pref_in_app_store_key"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_tunnel"
|
||||
android:key="@string/pref_tunnel_key">
|
||||
|
|
144
src/com/android/vending/billing/IInAppBillingService.aidl
Normal file
144
src/com/android/vending/billing/IInAppBillingService.aidl
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.vending.billing;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
|
||||
* This service provides the following features:
|
||||
* 1. Provides a new API to get details of in-app items published for the app including
|
||||
* price, type, title and description.
|
||||
* 2. The purchase flow is synchronous and purchase information is available immediately
|
||||
* after it completes.
|
||||
* 3. Purchase information of in-app purchases is maintained within the Google Play system
|
||||
* till the purchase is consumed.
|
||||
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
|
||||
* in-app items are consumable and thereafter can be purchased again.
|
||||
* 5. An API to get current purchases of the user immediately. This will not contain any
|
||||
* consumed purchases.
|
||||
*
|
||||
* All calls will give a response code with the following possible values
|
||||
* RESULT_OK = 0 - success
|
||||
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
|
||||
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
|
||||
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
|
||||
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
|
||||
* RESULT_ERROR = 6 - Fatal error during the API action
|
||||
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
|
||||
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
|
||||
*/
|
||||
interface IInAppBillingService {
|
||||
/**
|
||||
* Checks support for the requested billing API version, package and in-app type.
|
||||
* Minimum API version supported by this interface is 3.
|
||||
* @param apiVersion the billing version which the app is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param type type of the in-app item being purchased "inapp" for one-time purchases
|
||||
* and "subs" for subscription.
|
||||
* @return RESULT_OK(0) on success, corresponding result code on failures
|
||||
*/
|
||||
int isBillingSupported(int apiVersion, String packageName, String type);
|
||||
|
||||
/**
|
||||
* Provides details of a list of SKUs
|
||||
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
|
||||
* with a list JSON strings containing the productId, price, title and description.
|
||||
* This API can be called with a maximum of 20 SKUs.
|
||||
* @param apiVersion billing API version that the Third-party is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "DETAILS_LIST" with a StringArrayList containing purchase information
|
||||
* in JSON format similar to:
|
||||
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
|
||||
* "title : "Example Title", "description" : "This is an example description" }'
|
||||
*/
|
||||
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
|
||||
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
|
||||
* the type, a unique purchase token and an optional developer payload.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param sku the SKU of the in-app item as published in the developer console
|
||||
* @param type the type of the in-app item ("inapp" for one-time purchases
|
||||
* and "subs" for subscription).
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "BUY_INTENT" - PendingIntent to start the purchase flow
|
||||
*
|
||||
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
|
||||
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
|
||||
* If the purchase is successful, the result data will contain the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
|
||||
* '{"orderId":"12999763169054705758.1371079406387615",
|
||||
* "packageName":"com.example.app",
|
||||
* "productId":"exampleSku",
|
||||
* "purchaseTime":1345678900000,
|
||||
* "purchaseToken" : "122333444455555",
|
||||
* "developerPayload":"example developer payload" }'
|
||||
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
|
||||
* was signed with the private key of the developer
|
||||
* TODO: change this to app-specific keys.
|
||||
*/
|
||||
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
|
||||
String developerPayload);
|
||||
|
||||
/**
|
||||
* Returns the current SKUs owned by the user of the type and package name specified along with
|
||||
* purchase information and a signature of the data to be validated.
|
||||
* This will return all SKUs that have been purchased in V3 and managed items purchased using
|
||||
* V1 and V2 that have not been consumed.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param type the type of the in-app items being requested
|
||||
* ("inapp" for one-time purchases and "subs" for subscription).
|
||||
* @param continuationToken to be set as null for the first call, if the number of owned
|
||||
* skus are too many, a continuationToken is returned in the response bundle.
|
||||
* This method can be called again with the continuation token to get the next set of
|
||||
* owned skus.
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
|
||||
* failure as listed above.
|
||||
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
|
||||
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
|
||||
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
|
||||
* of the purchase information
|
||||
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
|
||||
* next set of in-app purchases. Only set if the
|
||||
* user has more owned skus than the current list.
|
||||
*/
|
||||
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
|
||||
|
||||
/**
|
||||
* Consume the last purchase of the given SKU. This will result in this item being removed
|
||||
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param purchaseToken token in the purchase information JSON that identifies the purchase
|
||||
* to be consumed
|
||||
* @return 0 if consumption succeeded. Appropriate error values for failures.
|
||||
*/
|
||||
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
|
||||
}
|
|
@ -19,13 +19,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
*/
|
||||
|
||||
import org.linphone.core.LinphoneCore;
|
||||
import org.linphone.core.LinphoneCore.LogCollectionUploadState;
|
||||
import org.linphone.core.LinphoneCoreListenerBase;
|
||||
import org.linphone.mediastream.Log;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
@ -38,6 +47,9 @@ public class AboutFragment extends Fragment implements OnClickListener {
|
|||
View sendLogButton = null;
|
||||
View resetLogButton = null;
|
||||
ImageView cancel;
|
||||
LinphoneCoreListenerBase mListener;
|
||||
private ProgressDialog progress;
|
||||
private boolean uploadInProgress;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
|
@ -62,22 +74,82 @@ public class AboutFragment extends Fragment implements OnClickListener {
|
|||
resetLogButton = view.findViewById(R.id.reset_log);
|
||||
resetLogButton.setOnClickListener(this);
|
||||
resetLogButton.setVisibility(org.linphone.LinphonePreferences.instance().isDebugEnabled() ? View.VISIBLE : View.GONE);
|
||||
|
||||
mListener = new LinphoneCoreListenerBase() {
|
||||
@Override
|
||||
public void uploadProgressIndication(LinphoneCore lc, int offset, int total) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadStateChanged(LinphoneCore lc, LogCollectionUploadState state, String info) {
|
||||
if (state == LogCollectionUploadState.LogCollectionUploadStateInProgress) {
|
||||
displayUploadLogsInProgress();
|
||||
} else if (state == LogCollectionUploadState.LogCollectionUploadStateDelivered || state == LogCollectionUploadState.LogCollectionUploadStateNotDelivered) {
|
||||
uploadInProgress = false;
|
||||
if (progress != null) progress.dismiss();
|
||||
if (state == LogCollectionUploadState.LogCollectionUploadStateDelivered) {
|
||||
sendLogs(LinphoneService.instance().getApplicationContext(), info);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void displayUploadLogsInProgress() {
|
||||
if (uploadInProgress) {
|
||||
return;
|
||||
}
|
||||
uploadInProgress = true;
|
||||
|
||||
progress = ProgressDialog.show(LinphoneActivity.instance(), null, null);
|
||||
Drawable d = new ColorDrawable(getResources().getColor(R.color.colorE));
|
||||
d.setAlpha(200);
|
||||
progress.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
|
||||
progress.getWindow().setBackgroundDrawable(d);
|
||||
progress.setContentView(R.layout.progress_dialog);
|
||||
progress.show();
|
||||
}
|
||||
|
||||
private void sendLogs(Context context, String info){
|
||||
final String appName = context.getString(R.string.app_name);
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_SEND);
|
||||
i.putExtra(Intent.EXTRA_EMAIL, new String[]{ context.getString(R.string.about_bugreport_email) });
|
||||
i.putExtra(Intent.EXTRA_SUBJECT, appName + " Logs");
|
||||
i.putExtra(Intent.EXTRA_TEXT, info);
|
||||
i.setType("application/zip");
|
||||
|
||||
try {
|
||||
startActivity(Intent.createChooser(i, "Send mail..."));
|
||||
} catch (android.content.ActivityNotFoundException ex) {
|
||||
Log.e(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.addListener(mListener);
|
||||
}
|
||||
|
||||
if (org.linphone.LinphoneActivity.isInstanciated()) {
|
||||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.ABOUT);
|
||||
}
|
||||
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -176,7 +176,7 @@ public class AccountPreferencesFragment extends PreferencesListFragment {
|
|||
}
|
||||
preference.setSummary(newValue.toString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
OnPreferenceChangeListener prefixChangedListener = new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
|
@ -234,6 +234,14 @@ public class AccountPreferencesFragment extends PreferencesListFragment {
|
|||
return true;
|
||||
}
|
||||
};
|
||||
OnPreferenceChangeListener friendlistSubscribeListener = new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
boolean value = (Boolean) newValue;
|
||||
LinphoneManager.getInstance().subscribeFriendList(value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
OnPreferenceChangeListener disableChangedListener = new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
|
@ -365,6 +373,12 @@ public class AccountPreferencesFragment extends PreferencesListFragment {
|
|||
if(!isNewAccount){
|
||||
escape.setChecked(mPrefs.getReplacePlusByZeroZero(n));
|
||||
}
|
||||
|
||||
CheckBoxPreference friendlistSubscribe = (CheckBoxPreference) advanced.getPreference(8);
|
||||
friendlistSubscribe.setOnPreferenceChangeListener(friendlistSubscribeListener);
|
||||
if(!isNewAccount){
|
||||
escape.setChecked(mPrefs.getReplacePlusByZeroZero(n));
|
||||
}
|
||||
|
||||
PreferenceCategory manage = (PreferenceCategory) getPreferenceScreen().findPreference(getString(R.string.pref_manage_key));
|
||||
final CheckBoxPreference disable = (CheckBoxPreference) manage.getPreference(0);
|
||||
|
|
|
@ -24,6 +24,8 @@ import org.linphone.core.LinphoneAddress;
|
|||
import org.linphone.core.LinphoneCall;
|
||||
import org.linphone.core.LinphoneCall.State;
|
||||
import org.linphone.core.LinphoneCallParams;
|
||||
import org.linphone.core.LinphoneChatMessage;
|
||||
import org.linphone.core.LinphoneChatRoom;
|
||||
import org.linphone.core.LinphoneCore;
|
||||
import org.linphone.core.LinphoneCoreException;
|
||||
import org.linphone.core.LinphoneCoreListenerBase;
|
||||
|
@ -91,6 +93,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
|
|||
private Handler mControlsHandler = new Handler();
|
||||
private Runnable mControls;
|
||||
private ImageView switchCamera;
|
||||
private TextView missedChats;
|
||||
private RelativeLayout mActiveCallHeader, sideMenuContent, avatar_layout;
|
||||
private ImageView pause, hangUp, dialer, video, micro, speaker, options, addCall, transfer, conference, conferenceStatus, contactPicture;
|
||||
private ImageView audioRoute, routeSpeaker, routeEarpiece, routeBluetooth, menu, chat;
|
||||
|
@ -161,7 +164,12 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
|
|||
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
|
||||
mProximity = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
|
||||
|
||||
mListener = new LinphoneCoreListenerBase(){
|
||||
mListener = new LinphoneCoreListenerBase() {
|
||||
@Override
|
||||
public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) {
|
||||
displayMissedChats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callState(LinphoneCore lc, final LinphoneCall call, LinphoneCall.State state, String message) {
|
||||
if (LinphoneManager.getLc().getCallsNb() == 0) {
|
||||
|
@ -362,6 +370,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
|
|||
|
||||
chat = (ImageView) findViewById(R.id.chat);
|
||||
chat.setOnClickListener(this);
|
||||
missedChats = (TextView) findViewById(R.id.missed_chats);
|
||||
|
||||
//Others
|
||||
|
||||
|
@ -500,6 +509,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
|
|||
refreshInCallActions();
|
||||
refreshCallList(getResources());
|
||||
enableAndRefreshInCallActions();
|
||||
displayMissedChats();
|
||||
}
|
||||
|
||||
private void refreshInCallActions() {
|
||||
|
@ -1806,4 +1816,26 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
|
|||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
|
||||
}
|
||||
|
||||
private void displayMissedChats() {
|
||||
int count = 0;
|
||||
LinphoneChatRoom[] chats = LinphoneManager.getLc().getChatRooms();
|
||||
for (LinphoneChatRoom chatroom : chats) {
|
||||
count += chatroom.getUnreadMessagesCount();
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
missedChats.setText(count + "");
|
||||
missedChats.setVisibility(View.VISIBLE);
|
||||
if (!isAnimationDisabled) {
|
||||
missedChats.startAnimation(AnimationUtils.loadAnimation(this, R.anim.bounce));
|
||||
}
|
||||
if(count > 99){
|
||||
//TODO
|
||||
}
|
||||
} else {
|
||||
missedChats.clearAnimation();
|
||||
missedChats.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,13 +213,8 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener
|
|||
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CONTACT_DETAIL);
|
||||
LinphoneActivity.instance().hideTabBar(false);
|
||||
}
|
||||
if (contact.getFullName() == null || contact.getFullName().equals("")) {
|
||||
//Contact has been deleted, return
|
||||
LinphoneActivity.instance().displayContacts(false);
|
||||
} else {
|
||||
contact.refresh();
|
||||
displayContact(inflater, view);
|
||||
}
|
||||
contact.refresh();
|
||||
displayContact(inflater, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -385,7 +385,7 @@ public class ContactEditorFragment extends Fragment {
|
|||
if (contact != null) {
|
||||
for (LinphoneNumberOrAddress numberOrAddress : contact.getNumbersOrAddresses()) {
|
||||
if (!numberOrAddress.isSIPAddress()) {
|
||||
View view = displayNumberOrAddress(controls, numberOrAddress.getValue());
|
||||
View view = displayNumberOrAddress(controls, numberOrAddress.getValue(), false);
|
||||
if (view != null)
|
||||
controls.addView(view);
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ public class ContactEditorFragment extends Fragment {
|
|||
if (newSipOrNumberToAdd != null) {
|
||||
boolean isSip = LinphoneUtils.isStrictSipAddress(newSipOrNumberToAdd) || !LinphoneUtils.isNumberAddress(newSipOrNumberToAdd);
|
||||
if(!isSip) {
|
||||
View view = displayNumberOrAddress(controls, newSipOrNumberToAdd);
|
||||
View view = displayNumberOrAddress(controls, newSipOrNumberToAdd, false);
|
||||
if (view != null)
|
||||
controls.addView(view);
|
||||
}
|
||||
|
@ -415,7 +415,7 @@ public class ContactEditorFragment extends Fragment {
|
|||
if (contact != null) {
|
||||
for (LinphoneNumberOrAddress numberOrAddress : contact.getNumbersOrAddresses()) {
|
||||
if (numberOrAddress.isSIPAddress()) {
|
||||
View view = displayNumberOrAddress(controls, numberOrAddress.getValue());
|
||||
View view = displayNumberOrAddress(controls, numberOrAddress.getValue(), true);
|
||||
if (view != null)
|
||||
controls.addView(view);
|
||||
}
|
||||
|
@ -425,7 +425,7 @@ public class ContactEditorFragment extends Fragment {
|
|||
if (newSipOrNumberToAdd != null) {
|
||||
boolean isSip = LinphoneUtils.isStrictSipAddress(newSipOrNumberToAdd) || !LinphoneUtils.isNumberAddress(newSipOrNumberToAdd);
|
||||
if (isSip) {
|
||||
View view = displayNumberOrAddress(controls, newSipOrNumberToAdd);
|
||||
View view = displayNumberOrAddress(controls, newSipOrNumberToAdd, true);
|
||||
if (view != null)
|
||||
controls.addView(view);
|
||||
}
|
||||
|
@ -438,35 +438,33 @@ public class ContactEditorFragment extends Fragment {
|
|||
return controls;
|
||||
}
|
||||
|
||||
private View displayNumberOrAddress(final LinearLayout controls, String numberOrAddress) {
|
||||
return displayNumberOrAddress(controls, numberOrAddress, false);
|
||||
private View displayNumberOrAddress(final LinearLayout controls, String numberOrAddress, boolean isSIP) {
|
||||
return displayNumberOrAddress(controls, numberOrAddress, isSIP, false);
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
private View displayNumberOrAddress(final LinearLayout controls, String numberOrAddress, boolean forceAddNumber) {
|
||||
boolean isSip = LinphoneUtils.isStrictSipAddress(numberOrAddress) || !LinphoneUtils.isNumberAddress(numberOrAddress);
|
||||
|
||||
if (isSip) {
|
||||
private View displayNumberOrAddress(final LinearLayout controls, String numberOrAddress, boolean isSIP, boolean forceAddNumber) {
|
||||
if (isSIP) {
|
||||
if (firstSipAddressIndex == -1) {
|
||||
firstSipAddressIndex = controls.getChildCount();
|
||||
}
|
||||
numberOrAddress = numberOrAddress.replace("sip:", "");
|
||||
}
|
||||
if ((getResources().getBoolean(R.bool.hide_phone_numbers_in_editor) && !isSip) || (getResources().getBoolean(R.bool.hide_sip_addresses_in_editor) && isSip)) {
|
||||
if ((getResources().getBoolean(R.bool.hide_phone_numbers_in_editor) && !isSIP) || (getResources().getBoolean(R.bool.hide_sip_addresses_in_editor) && isSIP)) {
|
||||
if (forceAddNumber)
|
||||
isSip = !isSip; // If number can't be displayed because we hide a sort of number, change that category
|
||||
isSIP = !isSIP; // If number can't be displayed because we hide a sort of number, change that category
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
LinphoneNumberOrAddress tempNounoa;
|
||||
if (forceAddNumber) {
|
||||
tempNounoa = new LinphoneNumberOrAddress(null, isSip);
|
||||
tempNounoa = new LinphoneNumberOrAddress(null, isSIP);
|
||||
} else {
|
||||
if(isNewContact || newSipOrNumberToAdd != null) {
|
||||
tempNounoa = new LinphoneNumberOrAddress(numberOrAddress, isSip);
|
||||
tempNounoa = new LinphoneNumberOrAddress(numberOrAddress, isSIP);
|
||||
} else {
|
||||
tempNounoa = new LinphoneNumberOrAddress(null, isSip, numberOrAddress);
|
||||
tempNounoa = new LinphoneNumberOrAddress(null, isSIP, numberOrAddress);
|
||||
}
|
||||
}
|
||||
final LinphoneNumberOrAddress nounoa = tempNounoa;
|
||||
|
@ -475,7 +473,7 @@ public class ContactEditorFragment extends Fragment {
|
|||
final View view = inflater.inflate(R.layout.contact_edit_row, null);
|
||||
|
||||
final EditText noa = (EditText) view.findViewById(R.id.numoraddr);
|
||||
noa.setInputType(isSip ? InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS : InputType.TYPE_CLASS_PHONE);
|
||||
noa.setInputType(isSIP ? InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS : InputType.TYPE_CLASS_PHONE);
|
||||
noa.setText(numberOrAddress);
|
||||
noa.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
|
@ -496,7 +494,7 @@ public class ContactEditorFragment extends Fragment {
|
|||
}
|
||||
|
||||
ImageView delete = (ImageView) view.findViewById(R.id.delete_field);
|
||||
if ((getResources().getBoolean(R.bool.allow_only_one_phone_number) && !isSip) || (getResources().getBoolean(R.bool.allow_only_one_sip_address) && isSip)) {
|
||||
if ((getResources().getBoolean(R.bool.allow_only_one_phone_number) && !isSIP) || (getResources().getBoolean(R.bool.allow_only_one_sip_address) && isSIP)) {
|
||||
delete.setVisibility(View.GONE);
|
||||
}
|
||||
delete.setOnClickListener(new OnClickListener() {
|
||||
|
|
|
@ -459,6 +459,7 @@ public class ContactsListFragment extends Fragment implements OnClickListener, O
|
|||
}
|
||||
|
||||
CheckBox delete = (CheckBox) view.findViewById(R.id.delete);
|
||||
ImageView linphoneFriend = (ImageView) view.findViewById(R.id.friendLinphone);
|
||||
|
||||
TextView name = (TextView) view.findViewById(R.id.name);
|
||||
name.setText(contact.getFullName());
|
||||
|
@ -474,7 +475,13 @@ public class ContactsListFragment extends Fragment implements OnClickListener, O
|
|||
separatorText.setText(String.valueOf(fullName.charAt(0)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(contact.isInLinphoneFriendList()){
|
||||
linphoneFriend.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
linphoneFriend.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
ImageView icon = (ImageView) view.findViewById(R.id.contact_picture);
|
||||
if (contact.hasPhoto()) {
|
||||
LinphoneUtils.setImagePictureFromUri(getActivity(), icon, contact.getPhotoUri(), contact.getThumbnailUri());
|
||||
|
|
|
@ -126,8 +126,10 @@ public class ContactsManager extends ContentObserver {
|
|||
search = search.toLowerCase(Locale.getDefault());
|
||||
List<LinphoneContact> searchContacts = new ArrayList<LinphoneContact>();
|
||||
for (LinphoneContact contact : contacts) {
|
||||
if (contact.getFullName().toLowerCase(Locale.getDefault()).contains(search)) {
|
||||
searchContacts.add(contact);
|
||||
if (contact.getFullName() != null) {
|
||||
if (contact.getFullName().toLowerCase(Locale.getDefault()).contains(search)) {
|
||||
searchContacts.add(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
return searchContacts;
|
||||
|
@ -137,8 +139,10 @@ public class ContactsManager extends ContentObserver {
|
|||
search = search.toLowerCase(Locale.getDefault());
|
||||
List<LinphoneContact> searchContacts = new ArrayList<LinphoneContact>();
|
||||
for (LinphoneContact contact : sipContacts) {
|
||||
if (contact.getFullName().toLowerCase(Locale.getDefault()).contains(search)) {
|
||||
searchContacts.add(contact);
|
||||
if (contact.getFullName() != null) {
|
||||
if (contact.getFullName().toLowerCase(Locale.getDefault()).contains(search)) {
|
||||
searchContacts.add(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
return searchContacts;
|
||||
|
|
|
@ -50,13 +50,16 @@ import org.linphone.ui.AddressText;
|
|||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
|
@ -126,6 +129,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
private ListView accountsList, sideMenuItemList;
|
||||
private ImageView menu;
|
||||
private boolean fetchedContactsOnce = false;
|
||||
private boolean doNotGoToCallActivity = false;
|
||||
|
||||
static final boolean isInstanciated() {
|
||||
return instance != null;
|
||||
|
@ -970,22 +974,6 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
startActivityForResult(intent, CALL_ACTIVITY);
|
||||
}
|
||||
|
||||
public void sendLogs(Context context, String info){
|
||||
final String appName = context.getString(R.string.app_name);
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_SEND);
|
||||
i.putExtra(Intent.EXTRA_EMAIL, new String[]{ context.getString(R.string.about_bugreport_email) });
|
||||
i.putExtra(Intent.EXTRA_SUBJECT, appName + " Logs");
|
||||
i.putExtra(Intent.EXTRA_TEXT, info);
|
||||
i.setType("application/zip");
|
||||
|
||||
try {
|
||||
startActivity(Intent.createChooser(i, "Send mail..."));
|
||||
} catch (android.content.ActivityNotFoundException ex) {
|
||||
Log.e(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a sensor to track phoneOrientation changes
|
||||
*/
|
||||
|
@ -1137,6 +1125,9 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
protected void onPause() {
|
||||
getIntent().putExtra("PreviousActivity", 0);
|
||||
|
||||
if(LinphonePreferences.instance().isFriendlistsubscriptionEnabled()){
|
||||
LinphoneManager.getInstance().subscribeFriendList(!isApplicationBroughtToBackground(this));
|
||||
}
|
||||
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null) {
|
||||
lc.removeListener(mListener);
|
||||
|
@ -1144,7 +1135,28 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
|
||||
public static boolean isApplicationBroughtToBackground(final Activity activity) {
|
||||
ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);
|
||||
|
||||
// Check the top Activity against the list of Activities contained in the Application's package.
|
||||
if (!tasks.isEmpty()) {
|
||||
ComponentName topActivity = tasks.get(0).topActivity;
|
||||
try {
|
||||
PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
for (ActivityInfo activityInfo : pi.activities) {
|
||||
if(topActivity.getClassName().equals(activityInfo.name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch( PackageManager.NameNotFoundException e) {
|
||||
return false; // Never happens.
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void checkAndRequestExternalStoragePermission() {
|
||||
if (LinphonePreferences.instance().writeExternalStoragePermAsked()) {
|
||||
return;
|
||||
|
@ -1186,7 +1198,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
|
||||
public void checkAndRequestPermission(String permission, int result) {
|
||||
if (getPackageManager().checkPermission(permission, getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this, new String[]{ permission }, result);
|
||||
ActivityCompat.requestPermissions(this, new String[]{permission}, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1259,12 +1271,15 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
refreshAccounts();
|
||||
|
||||
updateMissedChatCount();
|
||||
if(LinphonePreferences.instance().isFriendlistsubscriptionEnabled()){
|
||||
LinphoneManager.getInstance().subscribeFriendList(true);
|
||||
}
|
||||
|
||||
displayMissedCalls(LinphoneManager.getLc().getMissedCallsCount());
|
||||
|
||||
LinphoneManager.getInstance().changeStatusToOnline();
|
||||
|
||||
if (getIntent().getIntExtra("PreviousActivity", 0) != CALL_ACTIVITY){
|
||||
if (getIntent().getIntExtra("PreviousActivity", 0) != CALL_ACTIVITY && !doNotGoToCallActivity) {
|
||||
if (LinphoneManager.getLc().getCalls().length > 0) {
|
||||
LinphoneCall call = LinphoneManager.getLc().getCalls()[0];
|
||||
LinphoneCall.State callState = call.getState();
|
||||
|
@ -1289,6 +1304,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
}
|
||||
}
|
||||
}
|
||||
doNotGoToCallActivity = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1325,6 +1341,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
if (extras != null && extras.getBoolean("GoToChat", false)) {
|
||||
LinphoneService.instance().removeMessageNotification();
|
||||
String sipUri = extras.getString("ChatContactSipUri");
|
||||
doNotGoToCallActivity = true;
|
||||
displayChat(sipUri);
|
||||
} else if (extras != null && extras.getBoolean("Notification", false)) {
|
||||
if (LinphoneManager.getLc().getCallsNb() > 0) {
|
||||
|
@ -1349,19 +1366,6 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
|
|||
}
|
||||
}
|
||||
if (LinphoneManager.getLc().getCalls().length > 0) {
|
||||
LinphoneCall calls[] = LinphoneManager.getLc().getCalls();
|
||||
if (calls.length > 0) {
|
||||
LinphoneCall call = calls[0];
|
||||
|
||||
if (call != null && call.getState() != LinphoneCall.State.IncomingReceived) {
|
||||
if (call.getCurrentParamsCopy().getVideoEnabled()) {
|
||||
//startVideoActivity(call);
|
||||
} else {
|
||||
//startIncallActivity(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a call is ringing, start incomingcallactivity
|
||||
Collection<LinphoneCall.State> incoming = new ArrayList<LinphoneCall.State>();
|
||||
incoming.add(LinphoneCall.State.IncomingReceived);
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.linphone.core.LinphoneCoreException;
|
|||
import org.linphone.core.LinphoneCoreFactory;
|
||||
import org.linphone.core.LinphoneFriend;
|
||||
import org.linphone.core.LinphoneFriend.SubscribePolicy;
|
||||
import org.linphone.core.PresenceBasicStatus;
|
||||
import org.linphone.mediastream.Log;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
|
@ -203,12 +204,14 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
|
|||
}
|
||||
|
||||
if (isLinphoneFriend()) {
|
||||
if (!noa.getOldValue().startsWith("sip:")) {
|
||||
noa.setOldValue("sip:" + noa.getOldValue());
|
||||
if (noa.isSIPAddress()) {
|
||||
if (!noa.getOldValue().startsWith("sip:")) {
|
||||
noa.setOldValue("sip:" + noa.getOldValue());
|
||||
}
|
||||
}
|
||||
LinphoneNumberOrAddress toRemove = null;
|
||||
for (LinphoneNumberOrAddress address : addresses) {
|
||||
if (noa.getOldValue().equals(address.getValue())) {
|
||||
if (noa.getOldValue().equals(address.getValue()) && noa.isSIPAddress() == address.isSIPAddress()) {
|
||||
toRemove = address;
|
||||
break;
|
||||
}
|
||||
|
@ -274,15 +277,19 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
|
|||
}
|
||||
}
|
||||
if (isLinphoneFriend()) {
|
||||
if (!noa.getValue().startsWith("sip:")) {
|
||||
noa.setValue("sip:" + noa.getValue());
|
||||
if (noa.isSIPAddress()) {
|
||||
if (!noa.getValue().startsWith("sip:")) {
|
||||
noa.setValue("sip:" + noa.getValue());
|
||||
}
|
||||
}
|
||||
if (noa.getOldValue() != null) {
|
||||
if (!noa.getOldValue().startsWith("sip:")) {
|
||||
noa.setOldValue("sip:" + noa.getOldValue());
|
||||
if (noa.isSIPAddress()) {
|
||||
if (!noa.getOldValue().startsWith("sip:")) {
|
||||
noa.setOldValue("sip:" + noa.getOldValue());
|
||||
}
|
||||
}
|
||||
for (LinphoneNumberOrAddress address : addresses) {
|
||||
if (noa.getOldValue().equals(address.getValue())) {
|
||||
if (noa.getOldValue().equals(address.getValue()) && noa.isSIPAddress() == address.isSIPAddress()) {
|
||||
address.setValue(noa.getValue());
|
||||
break;
|
||||
}
|
||||
|
@ -313,24 +320,35 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
|
|||
}
|
||||
}
|
||||
if (isLinphoneFriend()) {
|
||||
boolean hasAddr = false;
|
||||
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc == null) return;
|
||||
|
||||
friend.edit();
|
||||
friend.setName(fullName);
|
||||
//TODO: handle removal of all existing SIP addresses
|
||||
for (LinphoneNumberOrAddress address : addresses) {
|
||||
try {
|
||||
// Currently we only support 1 address / friend
|
||||
LinphoneAddress addr = lc.interpretUrl(address.getValue());
|
||||
if (addr != null) {
|
||||
friend.setAddress(addr);
|
||||
for (LinphoneAddress address : friend.getAddresses()) {
|
||||
friend.removeAddress(address);
|
||||
}
|
||||
for (String phone : friend.getPhoneNumbers()) {
|
||||
friend.removePhoneNumber(phone);
|
||||
}
|
||||
for (LinphoneNumberOrAddress noa : addresses) {
|
||||
if (noa.isSIPAddress()) {
|
||||
try {
|
||||
LinphoneAddress addr = lc.interpretUrl(noa.getValue());
|
||||
if (addr != null) {
|
||||
friend.addAddress(addr);
|
||||
hasAddr = true;
|
||||
}
|
||||
} catch (LinphoneCoreException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
break;
|
||||
} catch (LinphoneCoreException e) {
|
||||
Log.e(e);
|
||||
} else {
|
||||
friend.addPhoneNumber(noa.getValue());
|
||||
}
|
||||
}
|
||||
if (hasAddr) {
|
||||
friend.setName(fullName);
|
||||
}
|
||||
friend.done();
|
||||
|
||||
if (friend.getAddress() != null) {
|
||||
|
@ -383,23 +401,25 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
|
|||
// Disable subscribes for now
|
||||
friend.enableSubscribes(false);
|
||||
friend.setIncSubscribePolicy(SubscribePolicy.SPDeny);
|
||||
friend.setName(fullName);
|
||||
if (hasSipAddress) {
|
||||
for (LinphoneNumberOrAddress noa : addresses) {
|
||||
if (noa.isSIPAddress()) {
|
||||
try {
|
||||
LinphoneAddress addr = LinphoneManager.getLc().interpretUrl(noa.getValue());
|
||||
if (addr != null) {
|
||||
friend.setAddress(addr);
|
||||
friend.addAddress(addr);
|
||||
}
|
||||
} catch (LinphoneCoreException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
} else {
|
||||
friend.addPhoneNumber(noa.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
||||
if (lc != null && friend.getAddress() != null) {
|
||||
friend.setName(fullName);
|
||||
try {
|
||||
lc.addFriend(friend);
|
||||
} catch (LinphoneCoreException e) {
|
||||
|
@ -427,6 +447,10 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
|
|||
return friend != null;
|
||||
}
|
||||
|
||||
public boolean isInLinphoneFriendList() {
|
||||
return (friend != null && friend.getPresenceModel() != null && friend.getPresenceModel().getBasicStatus().equals(PresenceBasicStatus.Open));
|
||||
}
|
||||
|
||||
public void setFriend(LinphoneFriend f) {
|
||||
friend = f;
|
||||
}
|
||||
|
|
|
@ -293,7 +293,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
|
|||
userAgent.append("LinphoneAndroid/" + mServiceContext.getPackageManager().getPackageInfo(mServiceContext.getPackageName(),0).versionCode);
|
||||
userAgent.append(" (");
|
||||
userAgent.append("Linphone/" + LinphoneManager.getLc().getVersion() + "; ");
|
||||
userAgent.append(Build.DEVICE + " " + Build.MODEL + " Android/" + Build.VERSION.SDK_INT);
|
||||
userAgent.append(Build.DEVICE + " " + Build.MODEL + " Android/" + Build.VERSION.SDK_INT);
|
||||
userAgent.append(")");
|
||||
return userAgent.toString();
|
||||
}
|
||||
|
@ -451,6 +451,15 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
|
|||
}
|
||||
}
|
||||
|
||||
public void subscribeFriendList(boolean enabled){
|
||||
LinphoneCore lc = getLcIfManagerNotDestroyedOrNull();
|
||||
if(lc != null ) {
|
||||
LinphoneFriendList mFriendList = (lc.getFriendLists())[0];
|
||||
mFriendList.enableSubscriptions(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static synchronized final LinphoneManager getInstance() {
|
||||
if (instance != null) return instance;
|
||||
|
||||
|
@ -739,6 +748,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
|
|||
mLc.setCallLogsDatabasePath(mCallLogDatabaseFile);
|
||||
mLc.setFriendsDatabasePath(mFriendsDatabaseFile);
|
||||
mLc.setUserCertificatesPath(mUserCertificatePath);
|
||||
subscribeFriendList(mPrefs.isFriendlistsubscriptionEnabled());
|
||||
//mLc.setCallErrorTone(Reason.NotFound, mErrorToneFile);
|
||||
|
||||
int availableCores = Runtime.getRuntime().availableProcessors();
|
||||
|
@ -969,8 +979,9 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
|
|||
Log.i("New global state [",state,"]");
|
||||
if (state == GlobalState.GlobalOn){
|
||||
try {
|
||||
initLiblinphone(lc);
|
||||
initOpenH264Helper();
|
||||
initLiblinphone(lc);
|
||||
initOpenH264Helper();
|
||||
|
||||
} catch (LinphoneCoreException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
@ -1105,7 +1116,6 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == State.CallUpdatedByRemote) {
|
||||
// If the correspondent proposes video while audio call
|
||||
boolean remoteVideo = call.getRemoteParams().getVideoEnabled();
|
||||
|
@ -1540,10 +1550,6 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
|
|||
@Override
|
||||
public void uploadStateChanged(LinphoneCore linphoneCore, LogCollectionUploadState state, String info) {
|
||||
Log.d("Log upload state: " + state.toString() + ", info = " + info);
|
||||
|
||||
if (state == LogCollectionUploadState.LogCollectionUploadStateDelivered) {
|
||||
LinphoneActivity.instance().sendLogs(LinphoneService.instance().getApplicationContext(),info);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -39,6 +40,7 @@ import org.linphone.core.LinphoneProxyConfig;
|
|||
import org.linphone.core.LpConfig;
|
||||
import org.linphone.core.TunnelConfig;
|
||||
import org.linphone.mediastream.Log;
|
||||
import org.linphone.purchase.Purchasable;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -662,6 +664,15 @@ public class LinphonePreferences {
|
|||
prxCfg.done();
|
||||
}
|
||||
|
||||
public boolean isFriendlistsubscriptionEnabled() {
|
||||
return getConfig().getBool("app", "friendlist_subscription_enabled", false);
|
||||
}
|
||||
|
||||
public void enabledFriendlistSubscription(boolean enabled) {
|
||||
getConfig().setBool("app", "friendlist_subscription_enabled", enabled);
|
||||
|
||||
}
|
||||
|
||||
public void setDefaultAccount(int accountIndex) {
|
||||
LinphoneProxyConfig[] prxCfgs = getLc().getProxyConfigList();
|
||||
if (accountIndex >= 0 && accountIndex < prxCfgs.length)
|
||||
|
@ -1228,7 +1239,44 @@ public class LinphonePreferences {
|
|||
}
|
||||
|
||||
public boolean isContactsMigrationDone(){
|
||||
return getConfig().getBool("app", "contacts_migration_done",false);
|
||||
return getConfig().getBool("app", "contacts_migration_done", false);
|
||||
}
|
||||
|
||||
public String getInAppPurchaseValidatingServerUrl() {
|
||||
return getConfig().getString("in-app-purchase", "server_url", null);
|
||||
}
|
||||
|
||||
public Purchasable getInAppPurchasedItem() {
|
||||
String id = getConfig().getString("in-app-purchase", "purchase_item_id", null);
|
||||
String payload = getConfig().getString("in-app-purchase", "purchase_item_payload", null);
|
||||
String signature = getConfig().getString("in-app-purchase", "purchase_item_signature", null);
|
||||
String username = getConfig().getString("in-app-purchase", "purchase_item_username", null);
|
||||
|
||||
Purchasable item = new Purchasable(id).setPayloadAndSignature(payload, signature).setUserData(username);
|
||||
return item;
|
||||
}
|
||||
|
||||
public void setInAppPurchasedItem(Purchasable item) {
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
getConfig().setString("in-app-purchase", "purchase_item_id", item.getId());
|
||||
getConfig().setString("in-app-purchase", "purchase_item_payload", item.getPayload());
|
||||
getConfig().setString("in-app-purchase", "purchase_item_signature", item.getPayloadSignature());
|
||||
getConfig().setString("in-app-purchase", "purchase_item_username", item.getUserData());
|
||||
}
|
||||
|
||||
public ArrayList<String> getInAppPurchasables() {
|
||||
ArrayList<String> purchasables = new ArrayList<String>();
|
||||
String list = getConfig().getString("in-app-purchase", "purchasable_items_ids", null);
|
||||
if (list != null) {
|
||||
for(String purchasable : list.split(";")) {
|
||||
if (purchasable.length() > 0) {
|
||||
purchasables.add(purchasable);
|
||||
}
|
||||
}
|
||||
}
|
||||
return purchasables;
|
||||
}
|
||||
|
||||
public String getXmlRpcServerUrl() {
|
||||
|
|
|
@ -34,8 +34,10 @@ import org.linphone.mediastream.Log;
|
|||
import org.linphone.mediastream.Version;
|
||||
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
|
||||
import org.linphone.tools.OpenH264DownloadHelper;
|
||||
import org.linphone.purchase.InAppPurchaseActivity;
|
||||
import org.linphone.ui.LedPreference;
|
||||
import org.linphone.ui.PreferencesListFragment;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
|
@ -54,6 +56,8 @@ import android.preference.PreferenceScreen;
|
|||
* @author Sylvain Berfini
|
||||
*/
|
||||
public class SettingsFragment extends PreferencesListFragment {
|
||||
private static final int WIZARD_INTENT = 1;
|
||||
private static final int STORE_INTENT = 2;
|
||||
private LinphonePreferences mPrefs;
|
||||
private Handler mHandler = new Handler();
|
||||
private LinphoneCoreListenerBase mListener;
|
||||
|
@ -114,6 +118,14 @@ public class SettingsFragment extends PreferencesListFragment {
|
|||
return true;
|
||||
}
|
||||
});
|
||||
findPreference(getString(R.string.pref_in_app_store_key)).setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent intent = new Intent(LinphoneService.instance(), InAppPurchaseActivity.class);
|
||||
startActivityForResult(intent, STORE_INTENT);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Sets listener for each preference to update the matching value in linphonecore
|
||||
|
@ -136,6 +148,11 @@ public class SettingsFragment extends PreferencesListFragment {
|
|||
hidePreference(R.string.pref_add_account_key);
|
||||
}
|
||||
|
||||
if(!getResources().getBoolean(R.bool.in_app_purchase_in_settings)){
|
||||
hidePreference(R.string.pref_in_app_store_key);
|
||||
}
|
||||
|
||||
|
||||
if (getResources().getBoolean(R.bool.disable_animations)) {
|
||||
uncheckAndHidePreference(R.string.pref_animation_enable_key);
|
||||
}
|
||||
|
|
|
@ -22,13 +22,16 @@ import org.linphone.R;
|
|||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
/**
|
||||
* @author Sylvain Berfini
|
||||
|
@ -36,16 +39,22 @@ import android.widget.Toast;
|
|||
public class LinphoneLoginFragment extends Fragment implements OnClickListener, TextWatcher {
|
||||
private EditText login, password, displayName;
|
||||
private Button apply;
|
||||
private TextView forgotPassword;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.assistant_linphone_login, container, false);
|
||||
|
||||
String url = "http://linphone.org/free-sip-service.html&action=recover";
|
||||
|
||||
login = (EditText) view.findViewById(R.id.assistant_username);
|
||||
login.addTextChangedListener(this);
|
||||
password = (EditText) view.findViewById(R.id.assistant_password);
|
||||
password.addTextChangedListener(this);
|
||||
forgotPassword = (TextView) view.findViewById(R.id.forgot_password);
|
||||
forgotPassword.setText(Html.fromHtml("<a href=\"" + url + "\"'>"+ getString(R.string.forgot_password) + "</a>"));
|
||||
forgotPassword.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
displayName = (EditText) view.findViewById(R.id.assistant_display_name);
|
||||
apply = (Button) view.findViewById(R.id.assistant_apply);
|
||||
apply.setEnabled(false);
|
||||
|
|
|
@ -88,7 +88,7 @@ public class RemoteProvisioningLoginActivity extends Activity implements OnClick
|
|||
private boolean storeAccount(String username, String password, String domain) {
|
||||
LinphoneCore lc = LinphoneManager.getLc();
|
||||
|
||||
XmlRpcHelper xmlRpcHelper = new XmlRpcHelper(null);
|
||||
XmlRpcHelper xmlRpcHelper = new XmlRpcHelper();
|
||||
xmlRpcHelper.getRemoteProvisioningFilenameAsync(new XmlRpcListenerBase() {
|
||||
@Override
|
||||
public void onRemoteProvisioningFilenameSent(String result) {
|
||||
|
|
240
src/org/linphone/purchase/InAppPurchaseActivity.java
Normal file
240
src/org/linphone/purchase/InAppPurchaseActivity.java
Normal file
|
@ -0,0 +1,240 @@
|
|||
package org.linphone.purchase;
|
||||
/*
|
||||
InAppPurchaseListener.java
|
||||
Copyright (C) 2015 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program 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 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.linphone.LinphoneManager;
|
||||
import org.linphone.R;
|
||||
import org.linphone.core.LinphoneProxyConfig;
|
||||
import org.linphone.mediastream.Log;
|
||||
import org.linphone.xmlrpc.XmlRpcHelper;
|
||||
import org.linphone.xmlrpc.XmlRpcListenerBase;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* @author Sylvain Berfini
|
||||
*/
|
||||
public class InAppPurchaseActivity extends Activity implements InAppPurchaseListener, OnClickListener {
|
||||
private InAppPurchaseHelper inAppPurchaseHelper;
|
||||
private LinearLayout purchasableItemsLayout;
|
||||
private ArrayList<Purchasable> purchasedItems;
|
||||
private Button buyItemButton, recoverAccountButton;
|
||||
private Handler mHandler = new Handler();
|
||||
|
||||
private EditText username, email;
|
||||
private TextView errorMessage;
|
||||
private boolean usernameOk = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
inAppPurchaseHelper = new InAppPurchaseHelper(this, this);
|
||||
|
||||
setContentView(R.layout.in_app_store);
|
||||
purchasableItemsLayout = (LinearLayout) findViewById(R.id.purchasable_items);
|
||||
|
||||
username = (EditText) findViewById(R.id.username);
|
||||
email = (EditText) findViewById(R.id.email);
|
||||
errorMessage = (TextView) findViewById(R.id.username_error);
|
||||
addUsernameHandler(username, errorMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
inAppPurchaseHelper.destroy();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceAvailableForQueries() {
|
||||
email.setText(inAppPurchaseHelper.getGmailAccount());
|
||||
email.setEnabled(false);
|
||||
|
||||
inAppPurchaseHelper.getPurchasedItemsAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailableItemsForPurchaseQueryFinished(ArrayList<Purchasable> items) {
|
||||
purchasableItemsLayout.removeAllViews();
|
||||
|
||||
for (Purchasable item : items) {
|
||||
displayBuySubscriptionButton(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPurchasedItemsQueryFinished(ArrayList<Purchasable> items) {
|
||||
purchasedItems = items;
|
||||
|
||||
if (items == null || items.size() == 0) {
|
||||
inAppPurchaseHelper.getAvailableItemsForPurchaseAsync();
|
||||
} else {
|
||||
for (Purchasable purchasedItem : purchasedItems) {
|
||||
Log.d("[In-app purchase] Found already bought item, expires " + purchasedItem.getExpireDate());
|
||||
displayRecoverAccountButton(purchasedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPurchasedItemConfirmationQueryFinished(boolean success) {
|
||||
if (success) {
|
||||
XmlRpcHelper xmlRpcHelper = new XmlRpcHelper();
|
||||
xmlRpcHelper.createAccountAsync(new XmlRpcListenerBase() {
|
||||
@Override
|
||||
public void onAccountCreated(String result) {
|
||||
//TODO
|
||||
}
|
||||
}, getUsername(), email.getText().toString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Purchasable item = (Purchasable) v.getTag();
|
||||
if (v.equals(recoverAccountButton)) {
|
||||
XmlRpcHelper xmlRpcHelper = new XmlRpcHelper();
|
||||
xmlRpcHelper.createAccountAsync(new XmlRpcListenerBase() {
|
||||
@Override
|
||||
public void onAccountCreated(String result) {
|
||||
//TODO
|
||||
}
|
||||
}, getUsername(), email.getText().toString(), null);
|
||||
} else {
|
||||
inAppPurchaseHelper.purchaseItemAsync(item.getId(), getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
inAppPurchaseHelper.parseAndVerifyPurchaseItemResultAsync(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecoverAccountSuccessful(boolean success) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
recoverAccountButton.setEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final String error) {
|
||||
Log.e(error);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(InAppPurchaseActivity.this, error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivateAccountSuccessful(boolean success) {
|
||||
if (success) {
|
||||
Log.d("[In-app purchase] Account activated");
|
||||
}
|
||||
}
|
||||
|
||||
private void displayBuySubscriptionButton(Purchasable item) {
|
||||
View layout = LayoutInflater.from(this).inflate(R.layout.in_app_purchasable, purchasableItemsLayout);
|
||||
Button button = (Button) layout.findViewById(R.id.inapp_button);
|
||||
button.setText("Buy account (" + item.getPrice() + ")");
|
||||
button.setTag(item);
|
||||
button.setOnClickListener(this);
|
||||
XmlRpcHelper xmlRpcHelper = new XmlRpcHelper();
|
||||
xmlRpcHelper.createAccountAsync(new XmlRpcListenerBase() {
|
||||
@Override
|
||||
public void onAccountCreated(String result) {
|
||||
//TODO
|
||||
}
|
||||
}, getUsername(), email.getText().toString(), null);
|
||||
|
||||
buyItemButton = button;
|
||||
buyItemButton.setEnabled(usernameOk);
|
||||
}
|
||||
|
||||
private void displayRecoverAccountButton(Purchasable item) {
|
||||
View layout = LayoutInflater.from(this).inflate(R.layout.in_app_purchasable, purchasableItemsLayout);
|
||||
Button button = (Button) layout.findViewById(R.id.inapp_button);
|
||||
button.setText("Recover account");
|
||||
button.setTag(item);
|
||||
button.setOnClickListener(this);
|
||||
|
||||
recoverAccountButton = button;
|
||||
recoverAccountButton.setEnabled(usernameOk);
|
||||
}
|
||||
|
||||
private String getUsername() {
|
||||
String username = this.username.getText().toString();
|
||||
LinphoneProxyConfig lpc = LinphoneManager.getLc().createProxyConfig();
|
||||
username = lpc.normalizePhoneNumber(username);
|
||||
return username.toLowerCase(Locale.getDefault());
|
||||
}
|
||||
|
||||
private boolean isUsernameCorrect(String username) {
|
||||
LinphoneProxyConfig lpc = LinphoneManager.getLc().createProxyConfig();
|
||||
return lpc.isPhoneNumber(username);
|
||||
}
|
||||
|
||||
private void addUsernameHandler(final EditText field, final TextView errorMessage) {
|
||||
field.addTextChangedListener(new TextWatcher() {
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {
|
||||
usernameOk = false;
|
||||
String username = s.toString();
|
||||
if (isUsernameCorrect(username)) {
|
||||
usernameOk = true;
|
||||
errorMessage.setText("");
|
||||
} else {
|
||||
errorMessage.setText(R.string.wizard_username_incorrect);
|
||||
}
|
||||
if (buyItemButton != null) buyItemButton.setEnabled(usernameOk);
|
||||
if (recoverAccountButton != null) recoverAccountButton.setEnabled(usernameOk);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
360
src/org/linphone/purchase/InAppPurchaseHelper.java
Normal file
360
src/org/linphone/purchase/InAppPurchaseHelper.java
Normal file
|
@ -0,0 +1,360 @@
|
|||
package org.linphone.purchase;
|
||||
/*
|
||||
InAppPurchaseHelper.java
|
||||
Copyright (C) 2015 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program 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 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.linphone.LinphonePreferences;
|
||||
import org.linphone.mediastream.Log;
|
||||
import org.linphone.xmlrpc.XmlRpcHelper;
|
||||
import org.linphone.xmlrpc.XmlRpcListenerBase;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Patterns;
|
||||
|
||||
import com.android.vending.billing.IInAppBillingService;
|
||||
|
||||
/**
|
||||
* @author Sylvain Berfini
|
||||
*/
|
||||
public class InAppPurchaseHelper {
|
||||
public static final int API_VERSION = 3;
|
||||
public static final int ACTIVITY_RESULT_CODE_PURCHASE_ITEM = 11089;
|
||||
|
||||
public static final String SKU_DETAILS_ITEM_LIST = "ITEM_ID_LIST";
|
||||
public static final String SKU_DETAILS_LIST = "DETAILS_LIST";
|
||||
public static final String SKU_DETAILS_PRODUCT_ID = "productId";
|
||||
public static final String SKU_DETAILS_PRICE = "price";
|
||||
public static final String SKU_DETAILS_TITLE = "title";
|
||||
public static final String SKU_DETAILS_DESC = "description";
|
||||
|
||||
public static final String ITEM_TYPE_INAPP = "inapp";
|
||||
public static final String ITEM_TYPE_SUBS = "subs";
|
||||
|
||||
public static final int RESPONSE_RESULT_OK = 0;
|
||||
public static final int RESULT_USER_CANCELED = 1;
|
||||
public static final int RESULT_SERVICE_UNAVAILABLE = 2;
|
||||
public static final int RESULT_BILLING_UNAVAILABLE = 3;
|
||||
public static final int RESULT_ITEM_UNAVAILABLE = 4;
|
||||
public static final int RESULT_DEVELOPER_ERROR = 5;
|
||||
public static final int RESULT_ERROR = 6;
|
||||
public static final int RESULT_ITEM_ALREADY_OWNED = 7;
|
||||
public static final int RESULT_ITEM_NOT_OWNED = 8;
|
||||
|
||||
public static final String RESPONSE_CODE = "RESPONSE_CODE";
|
||||
public static final String RESPONSE_BUY_INTENT = "BUY_INTENT";
|
||||
public static final String RESPONSE_INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA";
|
||||
public static final String RESPONSE_INAPP_SIGNATURE = "INAPP_DATA_SIGNATURE";
|
||||
public static final String RESPONSE_INAPP_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST";
|
||||
public static final String RESPONSE_INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST";
|
||||
public static final String RESPONSE_INAPP_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST";
|
||||
public static final String RESPONSE_INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN";
|
||||
|
||||
public static final String PURCHASE_DETAILS_PRODUCT_ID = "productId";
|
||||
public static final String PURCHASE_DETAILS_ORDER_ID = "orderId";
|
||||
public static final String PURCHASE_DETAILS_AUTO_RENEWING = "autoRenewing";
|
||||
public static final String PURCHASE_DETAILS_PURCHASE_TIME = "purchaseTime";
|
||||
public static final String PURCHASE_DETAILS_PURCHASE_STATE = "purchaseState";
|
||||
public static final String PURCHASE_DETAILS_PAYLOAD = "developerPayload";
|
||||
public static final String PURCHASE_DETAILS_PURCHASE_TOKEN = "purchaseToken";
|
||||
|
||||
public static final String CLIENT_ERROR_SUBSCRIPTION_PURCHASE_NOT_AVAILABLE = "SUBSCRIPTION_PURCHASE_NOT_AVAILABLE";
|
||||
public static final String CLIENT_ERROR_BIND_TO_BILLING_SERVICE_FAILED = "BIND_TO_BILLING_SERVICE_FAILED";
|
||||
public static final String CLIENT_ERROR_BILLING_SERVICE_UNAVAILABLE = "BILLING_SERVICE_UNAVAILABLE";
|
||||
|
||||
private Context mContext;
|
||||
private InAppPurchaseListener mListener;
|
||||
private IInAppBillingService mService;
|
||||
private ServiceConnection mServiceConn;
|
||||
private Handler mHandler = new Handler();
|
||||
private String mGmailAccount;
|
||||
|
||||
private String responseCodeToErrorMessage(int responseCode) {
|
||||
switch (responseCode) {
|
||||
case RESULT_USER_CANCELED:
|
||||
return "BILLING_RESPONSE_RESULT_USER_CANCELED";
|
||||
case RESULT_SERVICE_UNAVAILABLE:
|
||||
return "BILLING_RESPONSE_RESULT_SERVICE_UNAVAILABLE";
|
||||
case RESULT_BILLING_UNAVAILABLE:
|
||||
return "BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE";
|
||||
case RESULT_ITEM_UNAVAILABLE:
|
||||
return "BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE";
|
||||
case RESULT_DEVELOPER_ERROR:
|
||||
return "BILLING_RESPONSE_RESULT_DEVELOPER_ERROR";
|
||||
case RESULT_ERROR:
|
||||
return "BILLING_RESPONSE_RESULT_ERROR";
|
||||
case RESULT_ITEM_ALREADY_OWNED:
|
||||
return "BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED";
|
||||
case RESULT_ITEM_NOT_OWNED:
|
||||
return "BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED";
|
||||
}
|
||||
return "UNKNOWN_RESPONSE_CODE";
|
||||
}
|
||||
|
||||
public InAppPurchaseHelper(Activity context, InAppPurchaseListener listener) {
|
||||
mContext = context;
|
||||
mListener = listener;
|
||||
mGmailAccount = getGmailAccount();
|
||||
|
||||
mServiceConn = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mService = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mService = IInAppBillingService.Stub.asInterface(service);
|
||||
String packageName = mContext.getPackageName();
|
||||
try {
|
||||
int response = mService.isBillingSupported(API_VERSION, packageName, ITEM_TYPE_SUBS);
|
||||
if (response != RESPONSE_RESULT_OK || mGmailAccount == null) {
|
||||
Log.e("[In-app purchase] Error: Subscriptions aren't supported!");
|
||||
mListener.onError(CLIENT_ERROR_SUBSCRIPTION_PURCHASE_NOT_AVAILABLE);
|
||||
} else {
|
||||
mListener.onServiceAvailableForQueries();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
|
||||
serviceIntent.setPackage("com.android.vending");
|
||||
if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0).isEmpty()) {
|
||||
boolean ok = mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
|
||||
if (!ok) {
|
||||
Log.e("[In-app purchase] Error: Bind service failed");
|
||||
mListener.onError(CLIENT_ERROR_BIND_TO_BILLING_SERVICE_FAILED);
|
||||
}
|
||||
} else {
|
||||
Log.e("[In-app purchase] Error: Billing service unavailable on device.");
|
||||
mListener.onError(CLIENT_ERROR_BILLING_SERVICE_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<Purchasable> getAvailableItemsForPurchase() {
|
||||
ArrayList<Purchasable> products = new ArrayList<Purchasable>();
|
||||
ArrayList<String> skuList = LinphonePreferences.instance().getInAppPurchasables();
|
||||
Bundle querySkus = new Bundle();
|
||||
querySkus.putStringArrayList(SKU_DETAILS_ITEM_LIST, skuList);
|
||||
|
||||
Bundle skuDetails = null;
|
||||
try {
|
||||
skuDetails = mService.getSkuDetails(API_VERSION, mContext.getPackageName(), ITEM_TYPE_SUBS, querySkus);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
if (skuDetails != null) {
|
||||
int response = skuDetails.getInt(RESPONSE_CODE);
|
||||
if (response == RESPONSE_RESULT_OK) {
|
||||
ArrayList<String> responseList = skuDetails.getStringArrayList(SKU_DETAILS_LIST);
|
||||
for (String thisResponse : responseList) {
|
||||
try {
|
||||
JSONObject object = new JSONObject(thisResponse);
|
||||
String id = object.getString(SKU_DETAILS_PRODUCT_ID);
|
||||
String price = object.getString(SKU_DETAILS_PRICE);
|
||||
String title = object.getString(SKU_DETAILS_TITLE);
|
||||
String desc = object.getString(SKU_DETAILS_DESC);
|
||||
|
||||
Purchasable purchasable = new Purchasable(id).setTitle(title).setDescription(desc).setPrice(price);
|
||||
products.add(purchasable);
|
||||
} catch (JSONException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e("[In-app purchase] Error: responde code is not ok: " + responseCodeToErrorMessage(response));
|
||||
mListener.onError(responseCodeToErrorMessage(response));
|
||||
}
|
||||
}
|
||||
|
||||
return products;
|
||||
}
|
||||
|
||||
public void getAvailableItemsForPurchaseAsync() {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
final ArrayList<Purchasable> items = getAvailableItemsForPurchase();
|
||||
if (mHandler != null && mListener != null) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
mListener.onAvailableItemsForPurchaseQueryFinished(items);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void getPurchasedItemsAsync() {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
|
||||
final ArrayList<Purchasable> items = new ArrayList<Purchasable>();
|
||||
String continuationToken = null;
|
||||
do {
|
||||
Bundle purchasedItems = null;
|
||||
try {
|
||||
purchasedItems = mService.getPurchases(API_VERSION, mContext.getPackageName(), ITEM_TYPE_SUBS, continuationToken);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
if (purchasedItems != null) {
|
||||
int response = purchasedItems.getInt(RESPONSE_CODE);
|
||||
if (response == RESPONSE_RESULT_OK) {
|
||||
ArrayList<String> purchaseDataList = purchasedItems.getStringArrayList(RESPONSE_INAPP_PURCHASE_DATA_LIST);
|
||||
ArrayList<String> signatureList = purchasedItems.getStringArrayList(RESPONSE_INAPP_SIGNATURE_LIST);
|
||||
continuationToken = purchasedItems.getString(RESPONSE_INAPP_CONTINUATION_TOKEN);
|
||||
|
||||
for (int i = 0; i < purchaseDataList.size(); ++i) {
|
||||
String purchaseData = purchaseDataList.get(i);
|
||||
String signature = signatureList.get(i);
|
||||
Log.d("[In-app purchase] " + purchaseData);
|
||||
|
||||
Purchasable item = verifySignature(purchaseData, signature);
|
||||
if (item != null) {
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e("[In-app purchase] Error: responde code is not ok: " + responseCodeToErrorMessage(response));
|
||||
mListener.onError(responseCodeToErrorMessage(response));
|
||||
}
|
||||
}
|
||||
} while (continuationToken != null);
|
||||
|
||||
if (mHandler != null && mListener != null) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
mListener.onPurchasedItemsQueryFinished(items);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void parseAndVerifyPurchaseItemResultAsync(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == ACTIVITY_RESULT_CODE_PURCHASE_ITEM) {
|
||||
int responseCode = data.getIntExtra(RESPONSE_CODE, 0);
|
||||
|
||||
if (resultCode == Activity.RESULT_OK && responseCode == RESPONSE_RESULT_OK) {
|
||||
String payload = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
|
||||
String signature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);
|
||||
|
||||
XmlRpcHelper xmlRpcHelper = new XmlRpcHelper();
|
||||
xmlRpcHelper.verifySignatureAsync(new XmlRpcListenerBase() {
|
||||
@Override
|
||||
public void onSignatureVerified(boolean success) {
|
||||
mListener.onPurchasedItemConfirmationQueryFinished(success);
|
||||
}
|
||||
}, payload, signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void purchaseItem(String productId, String sipIdentity) {
|
||||
Bundle buyIntentBundle = null;
|
||||
try {
|
||||
buyIntentBundle = mService.getBuyIntent(API_VERSION, mContext.getPackageName(), productId, ITEM_TYPE_SUBS, sipIdentity);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
if (buyIntentBundle != null) {
|
||||
PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
|
||||
if (pendingIntent != null) {
|
||||
try {
|
||||
((Activity) mContext).startIntentSenderForResult(pendingIntent.getIntentSender(), ACTIVITY_RESULT_CODE_PURCHASE_ITEM, new Intent(), 0, 0, 0);
|
||||
} catch (SendIntentException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void purchaseItemAsync(final String productId, final String sipIdentity) {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
purchaseItem(productId, sipIdentity);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
mContext.unbindService(mServiceConn);
|
||||
}
|
||||
|
||||
public String getGmailAccount() {
|
||||
Account[] accounts = AccountManager.get(mContext).getAccountsByType("com.google");
|
||||
|
||||
for (Account account: accounts) {
|
||||
if (isEmailCorrect(account.name)) {
|
||||
String possibleEmail = account.name;
|
||||
return possibleEmail;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isEmailCorrect(String email) {
|
||||
Pattern emailPattern = Patterns.EMAIL_ADDRESS;
|
||||
return emailPattern.matcher(email).matches();
|
||||
}
|
||||
|
||||
private Purchasable verifySignature(String payload, String signature) {
|
||||
XmlRpcHelper helper = new XmlRpcHelper();
|
||||
if (helper.verifySignature(payload, signature)) {
|
||||
try {
|
||||
JSONObject json = new JSONObject(payload);
|
||||
String productId = json.getString(PURCHASE_DETAILS_PRODUCT_ID);
|
||||
Purchasable item = new Purchasable(productId);
|
||||
item.setPayloadAndSignature(payload, signature);
|
||||
return item;
|
||||
} catch (JSONException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
interface VerifiedSignatureListener {
|
||||
void onParsedAndVerifiedSignatureQueryFinished(Purchasable item);
|
||||
}
|
||||
}
|
67
src/org/linphone/purchase/InAppPurchaseListener.java
Normal file
67
src/org/linphone/purchase/InAppPurchaseListener.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
package org.linphone.purchase;
|
||||
/*
|
||||
InAppPurchaseListener.java
|
||||
Copyright (C) 2015 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program 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 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Sylvain Berfini
|
||||
*/
|
||||
public interface InAppPurchaseListener {
|
||||
/**
|
||||
* Callback called when the in-app purchase listener is connected and available for queries
|
||||
*/
|
||||
void onServiceAvailableForQueries();
|
||||
|
||||
/**
|
||||
* Callback called when the query for items available for purchase is done
|
||||
* @param items the list of items that can be purchased (also contains the ones already bought)
|
||||
*/
|
||||
void onAvailableItemsForPurchaseQueryFinished(ArrayList<Purchasable> items);
|
||||
|
||||
/**
|
||||
* Callback called when the query for items bought by the user is done
|
||||
* @param items the list of items already purchased by the user
|
||||
*/
|
||||
void onPurchasedItemsQueryFinished(ArrayList<Purchasable> items);
|
||||
|
||||
/**
|
||||
* Callback called when the purchase has been validated by our external server
|
||||
* @param success true if ok, false otherwise
|
||||
*/
|
||||
void onPurchasedItemConfirmationQueryFinished(boolean success);
|
||||
|
||||
/**
|
||||
* Callback called when the account has been recovered (or not)
|
||||
* @param success true if the recover has been successful, false otherwise
|
||||
*/
|
||||
void onRecoverAccountSuccessful(boolean success);
|
||||
|
||||
/**
|
||||
* Callback called when the account has been activated (or not)
|
||||
* @param success true if the activation has been successful, false otherwise
|
||||
*/
|
||||
void onActivateAccountSuccessful(boolean success);
|
||||
|
||||
/**
|
||||
* Callback called when an error occurred.
|
||||
* @param error the error that occurred
|
||||
*/
|
||||
void onError(String error);
|
||||
}
|
47
src/org/linphone/purchase/InAppPurchaseListenerBase.java
Normal file
47
src/org/linphone/purchase/InAppPurchaseListenerBase.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
package org.linphone.purchase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class InAppPurchaseListenerBase implements InAppPurchaseListener {
|
||||
@Override
|
||||
public void onServiceAvailableForQueries() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailableItemsForPurchaseQueryFinished(ArrayList<Purchasable> items) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPurchasedItemsQueryFinished(ArrayList<Purchasable> items) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPurchasedItemConfirmationQueryFinished(boolean success) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecoverAccountSuccessful(boolean success) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivateAccountSuccessful(boolean success) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
108
src/org/linphone/purchase/Purchasable.java
Normal file
108
src/org/linphone/purchase/Purchasable.java
Normal file
|
@ -0,0 +1,108 @@
|
|||
package org.linphone.purchase;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
/*
|
||||
Purchasable.java
|
||||
Copyright (C) 2015 Belledonne Communications, Grenoble, France
|
||||
|
||||
This program 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 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Sylvain Berfini
|
||||
*/
|
||||
public class Purchasable {
|
||||
private String id, title, description, price;
|
||||
private long expire;
|
||||
private String purchasePayload, purchasePayloadSignature;
|
||||
private String userData;
|
||||
|
||||
public Purchasable(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public Purchasable setTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Purchasable setDescription(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public Purchasable setPrice(String price) {
|
||||
this.price = price;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getExpire() {
|
||||
return expire;
|
||||
}
|
||||
|
||||
public String getExpireDate() {
|
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
|
||||
Date date = new Date(expire);
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
|
||||
public Purchasable setExpire(long expire) {
|
||||
this.expire = expire;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Purchasable setPayloadAndSignature(String payload, String signature) {
|
||||
this.purchasePayload = payload;
|
||||
this.purchasePayloadSignature = signature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPayload() {
|
||||
return this.purchasePayload;
|
||||
}
|
||||
|
||||
public String getPayloadSignature() {
|
||||
return this.purchasePayloadSignature;
|
||||
}
|
||||
|
||||
public Purchasable setUserData(String data) {
|
||||
this.userData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserData() {
|
||||
return this.userData;
|
||||
}
|
||||
}
|
|
@ -24,14 +24,10 @@ public class XmlRpcHelper {
|
|||
public static final String CLIENT_ERROR_SERVER_NOT_REACHABLE = "SERVER_NOT_REACHABLE";
|
||||
|
||||
private XMLRPCClient mXmlRpcClient;
|
||||
|
||||
public XmlRpcHelper(String serverUrl) {
|
||||
|
||||
public XmlRpcHelper() {
|
||||
try {
|
||||
if(serverUrl != null) {
|
||||
mXmlRpcClient = new XMLRPCClient(new URL(serverUrl));
|
||||
} else {
|
||||
mXmlRpcClient = new XMLRPCClient(new URL(LinphonePreferences.instance().getXmlRpcServerUrl()));
|
||||
}
|
||||
mXmlRpcClient = new XMLRPCClient(new URL(LinphonePreferences.instance().getInAppPurchaseValidatingServerUrl()));
|
||||
} catch (MalformedURLException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
@ -274,12 +270,11 @@ public class XmlRpcHelper {
|
|||
Log.e(error);
|
||||
listener.onError(error.toString());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResponse(long id, Object object) {
|
||||
String result = (String) object;
|
||||
String result = (String)object;
|
||||
Log.d("isAccountActivatedAsync: " + result);
|
||||
|
||||
if ("OK".equals(result)) {
|
||||
listener.onAccountActivatedFetched(true);
|
||||
return;
|
||||
|
@ -289,7 +284,6 @@ public class XmlRpcHelper {
|
|||
}
|
||||
listener.onAccountActivatedFetched(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(long id, XMLRPCException error) {
|
||||
Log.e(error);
|
||||
|
@ -859,26 +853,4 @@ public class XmlRpcHelper {
|
|||
listener.onError(CLIENT_ERROR_INVALID_SERVER_URL);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRemoteProvisioningFilename(String username, String domain, String password){
|
||||
if (mXmlRpcClient != null) {
|
||||
try {
|
||||
Object object = mXmlRpcClient.call("get_remote_provisioning_filename", username, domain, password);
|
||||
String result = (String)object;
|
||||
Log.d("getRemoteProvisioningFilename:: " + result);
|
||||
|
||||
if (result.startsWith("ERROR_")) {
|
||||
Log.e(result);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
|
||||
} catch (XMLRPCException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
} else {
|
||||
Log.e(CLIENT_ERROR_INVALID_SERVER_URL);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class XmlRpcListenerBase implements XmlRpcListener {
|
|||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRemoteProvisioningFilenameSent(String result) {
|
||||
// TODO Auto-generated method stub
|
||||
|
|
1
submodules/bcunit
Submodule
1
submodules/bcunit
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 53b8f5fed434857af9934b377f21adfc6c334bd6
|
|
@ -1 +1 @@
|
|||
Subproject commit 00dd3c003ed62cdd85f5f46261d5c050a943171e
|
||||
Subproject commit 374a1ae8be6eea497df54e90041b11d81bb32cdf
|
|
@ -1 +1 @@
|
|||
Subproject commit 0f209d9d5dff24a439aebf2ba3d3a06db97410dd
|
||||
Subproject commit 6c04a199ea95ce4caabac33ec7bbb4a7c8a39deb
|
|
@ -1 +1 @@
|
|||
Subproject commit 1bfb20e56f99ae77578d90850750c740a065c7fe
|
||||
Subproject commit 171ada8a3215767b94f419a5445e02a6144d079a
|
Loading…
Reference in a new issue