Added shortcuts to most recent 1-1 chat rooms

This commit is contained in:
Sylvain Berfini 2019-05-03 15:17:38 +02:00
parent 20c32ccdab
commit fdb1e0ed60
22 changed files with 318 additions and 52 deletions

View file

@ -10,6 +10,11 @@ Group changes to describe their impact on the project, as follows:
Fixed for any bug fixes.
Security to invite users to upgrade in case of vulnerabilities.
## [Unreleased]
### Added
- Added shortcuts to calls history & chat rooms list
## [4.1.0] - 2019-05-03
### Added

View file

@ -7,5 +7,5 @@
android:icon="@drawable/linphone_logo"
android:mimeType="vnd.android.cursor.item/vnd.%%PACKAGE_NAME%%.provider.sip_address"
android:summaryColumn="data2" />
<!-- You can use @string/linphone_address_mime_type above ! You have to hardcode it... -->
<!-- You can't use @string/linphone_address_mime_type above ! You have to hardcode it... -->
</ContactsSource>

View file

@ -134,6 +134,9 @@
android:name=".chat.ChatActivity"
android:launchMode="singleTop"
android:theme="@style/LinphoneStyleLight">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
@ -172,6 +175,9 @@
android:name=".history.HistoryActivity"
android:launchMode="singleTop"
android:theme="@style/LinphoneStyleLight">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<!-- Call activities -->

View file

@ -93,7 +93,7 @@ public class LinphoneManager implements SensorEventListener {
private final String mFriendsDatabaseFile;
private final String mUserCertsPath;
private final Context mServiceContext;
private final Context mContext;
private AndroidAudioManager mAudioManager;
private CallManager mCallManager;
private final PowerManager mPowerManager;
@ -120,7 +120,7 @@ public class LinphoneManager implements SensorEventListener {
public LinphoneManager(Context c) {
mExited = false;
mServiceContext = c;
mContext = c;
mBasePath = c.getFilesDir().getAbsolutePath();
mLPConfigXsd = mBasePath + "/lpconfig.xsd";
mLinphoneFactoryConfigFile = mBasePath + "/linphonerc";
@ -260,7 +260,7 @@ public class LinphoneManager implements SensorEventListener {
@Override
public void run() {
AlertDialog.Builder builder =
new AlertDialog.Builder(mServiceContext);
new AlertDialog.Builder(mContext);
builder.setMessage(
getString(R.string.update_available)
+ ": "
@ -279,8 +279,7 @@ public class LinphoneManager implements SensorEventListener {
Intent.ACTION_VIEW);
urlIntent.setData(
Uri.parse(urlToUse));
mServiceContext.startActivity(
urlIntent);
mContext.startActivity(urlIntent);
}
}
});
@ -416,12 +415,12 @@ public class LinphoneManager implements SensorEventListener {
Log.e("[Manager] Destroy Core Runtime Exception: " + e);
} finally {
try {
mServiceContext.unregisterReceiver(mHookReceiver);
mContext.unregisterReceiver(mHookReceiver);
} catch (Exception e) {
Log.e("[Manager] unregister receiver exception: " + e);
}
try {
mServiceContext.unregisterReceiver(mCallReceiver);
mContext.unregisterReceiver(mCallReceiver);
} catch (Exception e) {
Log.e("[Manager] unregister receiver exception: " + e);
}
@ -436,7 +435,7 @@ public class LinphoneManager implements SensorEventListener {
// traces alway start with traces enable to not missed first initialization
mCore =
Factory.instance()
.createCore(mConfigFile, mLinphoneFactoryConfigFile, mServiceContext);
.createCore(mConfigFile, mLinphoneFactoryConfigFile, mContext);
mCore.addListener(mCoreListener);
if (isPush) {
Log.w(
@ -472,12 +471,12 @@ public class LinphoneManager implements SensorEventListener {
private synchronized void initLiblinphone(Core core) {
mCore = core;
mAudioManager = new AndroidAudioManager(mServiceContext);
mAudioManager = new AndroidAudioManager(mContext);
mCore.setZrtpSecretsFile(mBasePath + "/zrtp_secrets");
String deviceName = mPrefs.getDeviceName(mServiceContext);
String appName = mServiceContext.getResources().getString(R.string.user_agent);
String deviceName = mPrefs.getDeviceName(mContext);
String appName = mContext.getResources().getString(R.string.user_agent);
String androidVersion = BuildConfig.VERSION_NAME;
String userAgent = appName + "/" + androidVersion + " (" + deviceName + ") LinphoneSDK";
@ -526,8 +525,8 @@ public class LinphoneManager implements SensorEventListener {
}
}
if (mServiceContext.getResources().getBoolean(R.bool.enable_push_id)) {
PushNotificationUtils.init(mServiceContext);
if (mContext.getResources().getBoolean(R.bool.enable_push_id)) {
PushNotificationUtils.init(mContext);
}
IntentFilter mCallIntentFilter =
@ -535,19 +534,19 @@ public class LinphoneManager implements SensorEventListener {
mCallIntentFilter.setPriority(99999999);
mCallReceiver = new OutgoingCallReceiver();
try {
mServiceContext.registerReceiver(mCallReceiver, mCallIntentFilter);
mContext.registerReceiver(mCallReceiver, mCallIntentFilter);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
mProximityWakelock =
mPowerManager.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
mServiceContext.getPackageName() + ";manager_proximity_sensor");
mContext.getPackageName() + ";manager_proximity_sensor");
IntentFilter mHookIntentFilter = new IntentFilter("com.base.module.phone.HOOKEVENT");
mHookIntentFilter.setPriority(999);
mHookReceiver = new HookReceiver();
mServiceContext.registerReceiver(mHookReceiver, mHookIntentFilter);
mContext.registerReceiver(mHookReceiver, mHookIntentFilter);
resetCameraFromPreferences();
@ -603,8 +602,7 @@ public class LinphoneManager implements SensorEventListener {
long future =
new Timestamp(
mServiceContext
.getResources()
mContext.getResources()
.getInteger(
R.integer.phone_number_linking_popup_time_interval))
.getTime();
@ -614,7 +612,7 @@ public class LinphoneManager implements SensorEventListener {
final Dialog dialog =
LinphoneUtils.getDialog(
mServiceContext,
mContext,
String.format(
getString(R.string.link_account_popup),
mCore.getDefaultProxyConfig()
@ -644,9 +642,8 @@ public class LinphoneManager implements SensorEventListener {
@Override
public void onClick(View view) {
Intent assistant = new Intent();
assistant.setClass(
mServiceContext, PhoneAccountLinkingAssistantActivity.class);
mServiceContext.startActivity(assistant);
assistant.setClass(mContext, PhoneAccountLinkingAssistantActivity.class);
mContext.startActivity(assistant);
dialog.dismiss();
}
});
@ -684,8 +681,8 @@ public class LinphoneManager implements SensorEventListener {
}
private void copyFromPackage(int ressourceId, String target) throws IOException {
FileOutputStream lOutputStream = mServiceContext.openFileOutput(target, 0);
InputStream lInputStream = mServiceContext.getResources().openRawResource(ressourceId);
FileOutputStream lOutputStream = mContext.openFileOutput(target, 0);
InputStream lInputStream = mContext.getResources().openRawResource(ressourceId);
int readByte;
byte[] buff = new byte[8048];
while ((readByte = lInputStream.read(buff)) != -1) {
@ -855,9 +852,7 @@ public class LinphoneManager implements SensorEventListener {
int lastTimestamp = LinphonePreferences.instance().getLastCheckReleaseTimestamp();
int currentTimeStamp = (int) System.currentTimeMillis();
int interval =
mServiceContext
.getResources()
.getInteger(R.integer.time_between_update_check); // 24h
mContext.getResources().getInteger(R.integer.time_between_update_check); // 24h
if (lastTimestamp == 0 || currentTimeStamp - lastTimestamp >= interval) {
mCore.checkForUpdate(BuildConfig.VERSION_NAME);
LinphonePreferences.instance().setLastCheckReleaseTimestamp(currentTimeStamp);
@ -894,7 +889,7 @@ public class LinphoneManager implements SensorEventListener {
}
private String getString(int key) {
return mServiceContext.getString(key);
return mContext.getString(key);
}
public boolean hasLastCallSasBeenRejected() {

View file

@ -32,18 +32,16 @@ import android.provider.ContactsContract;
import android.view.WindowManager;
import org.linphone.call.CallIncomingActivity;
import org.linphone.call.CallOutgoingActivity;
import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager;
import org.linphone.core.Call;
import org.linphone.core.Call.State;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory;
import org.linphone.core.GlobalState;
import org.linphone.core.LogLevel;
import org.linphone.core.LoggingService;
import org.linphone.core.LoggingServiceListener;
import org.linphone.core.ProxyConfig;
import org.linphone.core.RegistrationState;
import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version;
import org.linphone.notifications.NotificationsManager;
@ -173,14 +171,6 @@ public final class LinphoneService extends Service {
}
}
}
@Override
public void onGlobalStateChanged(
Core core, GlobalState state, String message) {}
@Override
public void onRegistrationStateChanged(
Core core, ProxyConfig cfg, RegistrationState state, String smessage) {}
};
}
@ -222,6 +212,8 @@ public final class LinphoneService extends Service {
mBluetoothManager = new BluetoothManager();
Compatibility.createChatShortcuts(this);
return START_STICKY;
}

View file

@ -28,6 +28,7 @@ import org.linphone.LinphoneService;
import org.linphone.R;
import org.linphone.assistant.MenuAssistantActivity;
import org.linphone.chat.ChatActivity;
import org.linphone.history.HistoryActivity;
import org.linphone.settings.LinphonePreferences;
/** Creates LinphoneService and wait until Core is ready to start main Activity */
@ -66,9 +67,15 @@ public class LinphoneLauncherActivity extends Activity {
if (useFirstLoginActivity && LinphonePreferences.instance().isFirstLaunch()) {
classToStart = MenuAssistantActivity.class;
} else {
if (getIntent().getExtras() != null
&& "Chat".equals(getIntent().getExtras().getString("Activity", null))) {
classToStart = ChatActivity.class;
if (getIntent().getExtras() != null) {
String activity = getIntent().getExtras().getString("Activity", null);
if (ChatActivity.NAME.equals(activity)) {
classToStart = ChatActivity.class;
} else if (HistoryActivity.NAME.equals(activity)) {
classToStart = HistoryActivity.class;
} else {
classToStart = DialerActivity.class;
}
} else {
classToStart = DialerActivity.class;
}

View file

@ -35,11 +35,13 @@ import org.linphone.core.tools.Log;
import org.linphone.utils.FileUtils;
public class ChatActivity extends MainActivity {
public static final String NAME = "Chat";
private String mSharedText, mSharedFiles;
@Override
protected void onCreate(Bundle savedInstanceState) {
getIntent().putExtra("Activity", "Chat");
getIntent().putExtra("Activity", NAME);
super.onCreate(savedInstanceState);
}

View file

@ -2,7 +2,7 @@ package org.linphone.compatibility;
/*
ApiTwentyEightPlus.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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

View file

@ -0,0 +1,100 @@
package org.linphone.compatibility;
/*
ApiTwentyFivePlus.java
Copyright (C) 2019 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import static java.lang.Math.min;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomCapabilities;
import org.linphone.utils.LinphoneShortcutManager;
@TargetApi(25)
class ApiTwentyFivePlus {
public static void createChatShortcuts(Context context) {
if (!context.getResources().getBoolean(R.bool.create_most_recent_chat_rooms_shortcuts))
return;
ShortcutManager shortcutManager =
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
ChatRoom[] rooms = LinphoneManager.getCore().getChatRooms();
ArrayList<ChatRoom> notEmptyOneToOneRooms = new ArrayList<>();
for (ChatRoom room : rooms) {
if (room.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
&& room.getHistorySize() > 0) {
notEmptyOneToOneRooms.add(room);
}
}
Collections.sort(
notEmptyOneToOneRooms,
new Comparator<ChatRoom>() {
public int compare(ChatRoom cr1, ChatRoom cr2) {
long timeDiff = cr1.getLastUpdateTime() - cr2.getLastUpdateTime();
if (timeDiff > 0) return -1;
else if (timeDiff == 0) return 0;
return 1;
}
});
LinphoneShortcutManager manager = new LinphoneShortcutManager(context);
int maxShortcuts =
min(notEmptyOneToOneRooms.size(), shortcutManager.getMaxShortcutCountPerActivity());
for (int i = 0; i < maxShortcuts; i++) {
// Android can only have around 4-5 shortcuts at a time
ChatRoom room = notEmptyOneToOneRooms.get(i);
ShortcutInfo shortcut = manager.createChatRoomShortcutInfo(room);
if (shortcut != null) {
shortcuts.add(shortcut);
}
}
shortcutManager.setDynamicShortcuts(shortcuts);
}
public static void updateShortcuts(Context context) {
if (!context.getResources().getBoolean(R.bool.create_most_recent_chat_rooms_shortcuts))
return;
ShortcutManager shortcutManager =
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
LinphoneShortcutManager manager = new LinphoneShortcutManager(context);
for (ShortcutInfo shortcutInfo : shortcutManager.getDynamicShortcuts()) {
ShortcutInfo shortcut = manager.updateShortcutInfo(shortcutInfo);
if (shortcut != null) {
shortcuts.add(shortcut);
}
}
shortcutManager.updateShortcuts(shortcuts);
}
}

View file

@ -2,7 +2,7 @@ package org.linphone.compatibility;
/*
ApiTwentyFourPlus.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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

View file

@ -2,7 +2,7 @@ package org.linphone.compatibility;
/*
ApiTwentyOnePlus.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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

View file

@ -2,7 +2,7 @@ package org.linphone.compatibility;
/*
ApiTwentySixPlus.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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

View file

@ -2,7 +2,7 @@ package org.linphone.compatibility;
/*
ApiTwentyThreePlus.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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

View file

@ -1,7 +1,7 @@
package org.linphone.compatibility;
/*
Compatibility.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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
@ -244,4 +244,16 @@ public class Compatibility {
}
return true;
}
public static void createChatShortcuts(Context context) {
if (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) {
ApiTwentyFivePlus.createChatShortcuts(context);
}
}
public static void updateShortcuts(Context context) {
if (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) {
ApiTwentyFivePlus.updateShortcuts(context);
}
}
}

View file

@ -2,7 +2,7 @@ package org.linphone.compatibility;
/*
CompatibilityScaleGestureDetector.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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

View file

@ -2,7 +2,7 @@ package org.linphone.compatibility;
/*
CompatibilityScaleGestureListener.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France
Copyright (C) 2017 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

View file

@ -475,5 +475,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
for (ContactsUpdatedListener listener : mContactsUpdatedListeners) {
listener.onContactsUpdated();
}
Compatibility.updateShortcuts(mContext);
}
}

View file

@ -32,8 +32,11 @@ import org.linphone.core.Address;
import org.linphone.utils.LinphoneUtils;
public class HistoryActivity extends MainActivity {
public static final String NAME = "History";
@Override
protected void onCreate(Bundle savedInstanceState) {
getIntent().putExtra("Activity", NAME);
super.onCreate(savedInstanceState);
}

View file

@ -118,6 +118,12 @@ public class NotificationsManager {
mListener =
new CoreListenerStub() {
@Override
public void onMessageSent(Core core, ChatRoom room, ChatMessage message) {
if (room.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
Compatibility.createChatShortcuts(mContext);
}
}
@Override
public void onMessageReceived(
@ -191,6 +197,10 @@ public class NotificationsManager {
createNotification(
cr, contact, from, textMessage, message.getTime(), null, null);
}
if (cr.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
Compatibility.createChatShortcuts(mContext);
}
}
};

View file

@ -0,0 +1,131 @@
package org.linphone.utils;
/*
LinphoneShortcutManager.java
Copyright (C) 2019 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.util.ArraySet;
import java.util.Set;
import org.linphone.LinphoneService;
import org.linphone.R;
import org.linphone.chat.ChatActivity;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address;
import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomCapabilities;
import org.linphone.core.Factory;
import org.linphone.core.tools.Log;
@TargetApi(25)
public class LinphoneShortcutManager {
private Context mContext;
private Set<String> mCategories;
public LinphoneShortcutManager(Context context) {
mContext = context;
mCategories = new ArraySet<>();
mCategories.add(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION);
}
public ShortcutInfo createChatRoomShortcutInfo(ChatRoom room) {
Address peerAddress =
room.hasCapability(ChatRoomCapabilities.Basic.toInt())
? room.getPeerAddress()
: room.getParticipants()[0].getAddress();
LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(peerAddress);
String address = peerAddress.asStringUriOnly();
Bitmap bm = null;
if (contact != null && contact.getThumbnailUri() != null) {
bm =
ImageUtils.getRoundBitmapFromUri(
LinphoneService.instance(), contact.getThumbnailUri());
}
Icon icon =
bm == null
? Icon.createWithResource(mContext, R.drawable.avatar)
: Icon.createWithBitmap(bm);
String name =
contact == null
? LinphoneUtils.getAddressDisplayName(peerAddress)
: contact.getFullName();
try {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(mContext, ChatActivity.class);
intent.addFlags(
Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.putExtra("RemoteSipUri", room.getPeerAddress().asStringUriOnly());
return new ShortcutInfo.Builder(mContext, address)
.setShortLabel(name)
.setIcon(icon)
.setCategories(mCategories)
.setIntent(intent)
.build();
} catch (Exception e) {
Log.e("[Shortcuts Manager] ShortcutInfo.Builder exception: " + e);
}
return null;
}
public ShortcutInfo updateShortcutInfo(ShortcutInfo shortcutInfo) {
String address = shortcutInfo.getId();
Address peerAddress = Factory.instance().createAddress(address);
LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(peerAddress);
if (contact != null) {
Bitmap bm = null;
if (contact != null && contact.getThumbnailUri() != null) {
bm =
ImageUtils.getRoundBitmapFromUri(
LinphoneService.instance(), contact.getThumbnailUri());
}
Icon icon =
bm == null
? Icon.createWithResource(mContext, R.drawable.avatar)
: Icon.createWithBitmap(bm);
String name =
contact == null
? LinphoneUtils.getAddressDisplayName(peerAddress)
: contact.getFullName();
try {
return new ShortcutInfo.Builder(mContext, address)
.setShortLabel(name)
.setIcon(icon)
.setCategories(mCategories)
.setIntent(shortcutInfo.getIntent())
.build();
} catch (Exception e) {
Log.e("[Shortcuts Manager] ShortcutInfo.Builder exception: " + e);
}
}
return shortcutInfo;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -96,6 +96,7 @@
<bool name="use_big_pictures_to_preview_images_file_transfers">true</bool>
<bool name="show_sip_uri_in_chat">false</bool>
<bool name="hide_empty_one_to_one_chat_rooms">true</bool>
<bool name="create_most_recent_chat_rooms_shortcuts">true</bool>
<!-- Contacts -->
<bool name="hide_contact_phone_numbers">false</bool>