/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of linphone-android * (see https://www.linphone.org). * * 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 3 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, see . */ package org.linphone.utils; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Handler; import android.os.Looper; import android.telephony.TelephonyManager; import android.text.Html; import android.text.Spanned; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import androidx.core.content.ContextCompat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import java.util.regex.Pattern; import org.linphone.LinphoneContext; import org.linphone.LinphoneManager; import org.linphone.R; import org.linphone.core.Address; import org.linphone.core.Call; import org.linphone.core.CallLog; import org.linphone.core.ChatMessage; import org.linphone.core.Content; import org.linphone.core.Core; import org.linphone.core.EventLog; import org.linphone.core.Factory; import org.linphone.core.LogCollectionState; import org.linphone.core.ProxyConfig; import org.linphone.core.tools.Log; import org.linphone.settings.LinphonePreferences; /** Helpers. */ public final class LinphoneUtils { private static final Handler sHandler = new Handler(Looper.getMainLooper()); private LinphoneUtils() {} public static void configureLoggingService(boolean isDebugEnabled, String appName) { if (!LinphonePreferences.instance().useJavaLogger()) { Factory.instance().enableLogCollection(LogCollectionState.Enabled); Factory.instance().setDebugMode(isDebugEnabled, appName); } else { Factory.instance().setDebugMode(isDebugEnabled, appName); Factory.instance() .enableLogCollection(LogCollectionState.EnabledWithoutPreviousLogHandler); if (isDebugEnabled) { if (LinphoneContext.isReady()) { Factory.instance() .getLoggingService() .addListener(LinphoneContext.instance().getJavaLoggingService()); } } else { if (LinphoneContext.isReady()) { Factory.instance() .getLoggingService() .removeListener(LinphoneContext.instance().getJavaLoggingService()); } } } } public static void dispatchOnUIThread(Runnable r) { sHandler.post(r); } public static void dispatchOnUIThreadAfter(Runnable r, long after) { sHandler.postDelayed(r, after); } public static void removeFromUIThreadDispatcher(Runnable r) { sHandler.removeCallbacks(r); } private static boolean isSipAddress(String numberOrAddress) { Factory.instance().createAddress(numberOrAddress); return true; } public static boolean isNumberAddress(String numberOrAddress) { ProxyConfig proxy = LinphoneManager.getCore().createProxyConfig(); return proxy.normalizePhoneNumber(numberOrAddress) != null; } public static boolean isStrictSipAddress(String numberOrAddress) { return isSipAddress(numberOrAddress) && numberOrAddress.startsWith("sip:"); } public static String getDisplayableAddress(Address addr) { return "sip:" + addr.getUsername() + "@" + addr.getDomain(); } public static String getAddressDisplayName(String uri) { Address lAddress; lAddress = Factory.instance().createAddress(uri); return getAddressDisplayName(lAddress); } public static String getAddressDisplayName(Address address) { if (address == null) return null; String displayName = address.getDisplayName(); if (displayName == null || displayName.isEmpty()) { displayName = address.getUsername(); } if (displayName == null || displayName.isEmpty()) { displayName = address.asStringUriOnly(); } return displayName; } public static String timestampToHumanDate(Context context, long timestamp, int format) { return timestampToHumanDate(context, timestamp, context.getString(format)); } public static String timestampToHumanDate(Context context, long timestamp, String format) { try { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(timestamp * 1000); // Core returns timestamps in seconds... SimpleDateFormat dateFormat; if (isToday(cal)) { dateFormat = new SimpleDateFormat( context.getResources().getString(R.string.today_date_format), Locale.getDefault()); } else { dateFormat = new SimpleDateFormat(format, Locale.getDefault()); } return dateFormat.format(cal.getTime()); } catch (NumberFormatException nfe) { return String.valueOf(timestamp); } } private static boolean isToday(Calendar cal) { return isSameDay(cal, Calendar.getInstance()); } private static boolean isSameDay(Calendar cal1, Calendar cal2) { if (cal1 == null || cal2 == null) { return false; } return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)); } private static boolean isCallRunning(Call call) { if (call == null) { return false; } Call.State state = call.getState(); return state == Call.State.Connected || state == Call.State.Updating || state == Call.State.UpdatedByRemote || state == Call.State.StreamsRunning || state == Call.State.Resuming; } public static boolean isCallEstablished(Call call) { if (call == null) { return false; } Call.State state = call.getState(); return isCallRunning(call) || state == Call.State.Paused || state == Call.State.PausedByRemote || state == Call.State.Pausing; } public static boolean isHighBandwidthConnection(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); return (info != null && info.isConnected() && isConnectionFast(info.getType(), info.getSubtype())); } private static boolean isConnectionFast(int type, int subType) { if (type == ConnectivityManager.TYPE_MOBILE) { switch (subType) { case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_GPRS: case TelephonyManager.NETWORK_TYPE_IDEN: return false; } } // in doubt, assume connection is good. return true; } public static void reloadVideoDevices() { Core core = LinphoneManager.getCore(); if (core == null) return; Log.i("[Utils] Reloading camera devices"); core.reloadVideoDevices(); LinphoneManager.getInstance().resetCameraFromPreferences(); } public static String getDisplayableUsernameFromAddress(String sipAddress) { String username = sipAddress; Core core = LinphoneManager.getCore(); if (core == null) return username; if (username.startsWith("sip:")) { username = username.substring(4); } if (username.contains("@")) { String[] split = username.split("@"); if (split.length > 1) { String domain = split[1]; ProxyConfig lpc = core.getDefaultProxyConfig(); if (lpc != null) { if (domain.equals(lpc.getDomain())) { return split[0]; } } else { if (domain.equals( LinphoneContext.instance() .getApplicationContext() .getString(R.string.default_domain))) { return split[0]; } } } return split[0]; } return username; } public static String getFullAddressFromUsername(String username) { String sipAddress = username; Core core = LinphoneManager.getCore(); if (core == null || username == null) return sipAddress; if (!sipAddress.startsWith("sip:")) { sipAddress = "sip:" + sipAddress; } if (!sipAddress.contains("@")) { ProxyConfig lpc = core.getDefaultProxyConfig(); if (lpc != null) { sipAddress = sipAddress + "@" + lpc.getDomain(); } else { sipAddress = sipAddress + "@" + LinphoneContext.instance() .getApplicationContext() .getString(R.string.default_domain); } } return sipAddress; } public static void displayErrorAlert(String msg, Context ctxt) { if (ctxt != null && msg != null) { AlertDialog.Builder builder = new AlertDialog.Builder(ctxt); builder.setMessage(msg) .setCancelable(false) .setNeutralButton(ctxt.getString(R.string.ok), null) .show(); } } public static Spanned getTextWithHttpLinks(String text) { if (text == null) return null; if (text.contains("<")) { text = text.replace("<", "<"); } if (text.contains(">")) { text = text.replace(">", ">"); } if (text.contains("\n")) { text = text.replace("\n", "
"); } if (text.contains("http://")) { int indexHttp = text.indexOf("http://"); int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp); String link = text.substring(indexHttp, indexFinHttp); String linkWithoutScheme = link.replace("http://", ""); text = text.replaceFirst( Pattern.quote(link), "" + linkWithoutScheme + ""); } if (text.contains("https://")) { int indexHttp = text.indexOf("https://"); int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp); String link = text.substring(indexHttp, indexFinHttp); String linkWithoutScheme = link.replace("https://", ""); text = text.replaceFirst( Pattern.quote(link), "" + linkWithoutScheme + ""); } return Html.fromHtml(text); } public static void showTrustDeniedDialog(Context context) { final Dialog dialog = new Dialog(context); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); Drawable d = new ColorDrawable(ContextCompat.getColor(context, R.color.dark_grey_color)); d.setAlpha(200); dialog.setContentView(R.layout.dialog); dialog.getWindow() .setLayout( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); dialog.getWindow().setBackgroundDrawable(d); TextView title = dialog.findViewById(R.id.dialog_title); title.setVisibility(View.GONE); TextView message = dialog.findViewById(R.id.dialog_message); message.setVisibility(View.VISIBLE); message.setText(context.getString(R.string.trust_denied)); ImageView icon = dialog.findViewById(R.id.dialog_icon); icon.setVisibility(View.VISIBLE); icon.setImageResource(R.drawable.security_alert_indicator); Button delete = dialog.findViewById(R.id.dialog_delete_button); delete.setVisibility(View.GONE); Button cancel = dialog.findViewById(R.id.dialog_cancel_button); cancel.setVisibility(View.VISIBLE); Button call = dialog.findViewById(R.id.dialog_ok_button); call.setVisibility(View.VISIBLE); call.setText(R.string.call); cancel.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); call.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { CallLog[] logs = LinphoneManager.getCore().getCallLogs(); CallLog lastLog = logs[0]; Address addressToCall = lastLog.getDir() == Call.Dir.Incoming ? lastLog.getFromAddress() : lastLog.getToAddress(); LinphoneManager.getCallManager() .newOutgoingCall(addressToCall.asString(), null); dialog.dismiss(); } }); dialog.show(); } public static Dialog getDialog(Context context, String text) { final Dialog dialog = new Dialog(context); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); Drawable d = new ColorDrawable(ContextCompat.getColor(context, R.color.dark_grey_color)); d.setAlpha(200); dialog.setContentView(R.layout.dialog); dialog.getWindow() .setLayout( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); dialog.getWindow().setBackgroundDrawable(d); TextView customText = dialog.findViewById(R.id.dialog_message); customText.setText(text); return dialog; } public static void deleteFileContentIfExists(EventLog eventLog) { if (eventLog.getType() == EventLog.Type.ConferenceChatMessage) { ChatMessage message = eventLog.getChatMessage(); if (message != null) { for (Content content : message.getContents()) { if (content.isFile() && content.getFilePath() != null) { Log.w( "[Linphone Utils] Chat message is being deleted, file ", content.getFilePath(), " will also be deleted"); FileUtils.deleteFile(content.getFilePath()); } } } } } }