diff --git a/res/drawable-xhdpi/chat_attachment.png b/res/drawable-xhdpi/chat_attachment.png
new file mode 100644
index 000000000..f46b54e12
Binary files /dev/null and b/res/drawable-xhdpi/chat_attachment.png differ
diff --git a/res/layout/chat_bubble.xml b/res/layout/chat_bubble.xml
index 181c85711..d26e87504 100644
--- a/res/layout/chat_bubble.xml
+++ b/res/layout/chat_bubble.xml
@@ -77,13 +77,53 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
-
+
+
+
+
+
+
+
+
+
+
+
italic
+
+
+
+
\ No newline at end of file
diff --git a/src/android/org/linphone/ChatFragment.java b/src/android/org/linphone/ChatFragment.java
index 39c179bbc..f049616a3 100644
--- a/src/android/org/linphone/ChatFragment.java
+++ b/src/android/org/linphone/ChatFragment.java
@@ -893,7 +893,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
//File transfer
private void pickImage() {
- //TODO : update to use with any file types
List cameraIntents = new ArrayList();
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory(), getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())));
@@ -902,7 +901,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
cameraIntents.add(captureIntent);
Intent galleryIntent = new Intent();
- //galleryIntent.setType("image/*");
galleryIntent.setType("*/*");
galleryIntent.setAction(Intent.ACTION_PICK);
@@ -1048,9 +1046,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
@Override
protected byte[] doInBackground(File... params) {
File file = params[0];
- //FileInputStream fileInputStream = null;
- //ByteArrayOutputStream stream = new ByteArrayOutputStream();
- //String extension = LinphoneUtils.getExtensionFromFileName(path);
byte[] byteArray = new byte[(int) file.length()];
FileInputStream fis = null;
@@ -1096,9 +1091,13 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
String fileToUploadPath = null;
if (data != null && data.getData() != null) {
fileToUploadPath = getRealPathFromURI(data.getData());
+ if(fileToUploadPath == null)
+ fileToUploadPath = data.getData().toString();
} else if (imageToUploadUri != null) {
fileToUploadPath = imageToUploadUri.getPath();
}
+ sendImageMessage(fileToUploadPath, 0);
+
} else {
super.onActivityResult(requestCode, resultCode, data);
}
@@ -1123,7 +1122,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
newChatConversation = false;
initChatRoom(sipUri);
- //TODO :
if(fileSharedUri != null){
//save SipUri into bundle
onSaveInstanceState(getArguments());
@@ -1215,6 +1213,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
public LinearLayout imdmLayout;
public ImageView imdmIcon;
public TextView imdmLabel;
+ public TextView fileExtensionLabel;
+ public TextView fileNameLabel;
public ViewHolder(View view) {
id = view.getId();
@@ -1234,6 +1234,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
imdmLayout = (LinearLayout) view.findViewById(R.id.imdmLayout);
imdmIcon = (ImageView) view.findViewById(R.id.imdmIcon);
imdmLabel = (TextView) view.findViewById(R.id.imdmText);
+ fileExtensionLabel = (TextView) view.findViewById(R.id.file_extension);
+ fileNameLabel = (TextView) view.findViewById(R.id.file_name);
}
@Override
@@ -1356,6 +1358,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
holder.delete.setVisibility(View.GONE);
holder.messageText.setVisibility(View.GONE);
holder.messageImage.setVisibility(View.GONE);
+ holder.fileExtensionLabel.setVisibility(View.GONE);
+ holder.fileNameLabel.setVisibility(View.GONE);
holder.fileTransferLayout.setVisibility(View.GONE);
holder.fileTransferProgressBar.setProgress(0);
holder.fileTransferAction.setEnabled(true);
@@ -1415,9 +1419,14 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
holder.imdmLayout.setVisibility(View.INVISIBLE);
}
+
+
+
+
if (externalBodyUrl != null || fileTransferContent != null) {
String appData = message.getAppData();
+
if (message.isOutgoing() && appData != null) {
holder.messageImage.setVisibility(View.VISIBLE);
if (!sameMessage) {
@@ -1457,6 +1466,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
loadBitmap(appData, holder.messageImage);
holder.messageImage.setTag(message.getAppData());
}
+
}
}
}
@@ -1494,10 +1504,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
public void onClick(View v) {
if (context.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
v.setEnabled(false);
- String extension = message.getFileTransferInformation().getSubtype();
- String filename = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())) + "." + extension;
+ String filename = message.getFileTransferInformation().getName();
+ String filename2 = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())) ; //+ "." + extension;
File file = new File(Environment.getExternalStorageDirectory(), filename);
- message.setAppData(filename);
+ message.setAppData(file.getPath());
LinphoneManager.getInstance().addDownloadMessagePending(message);
message.setListener(LinphoneManager.getInstance());
message.setFileTransferFilepath(file.getPath());
@@ -1536,6 +1546,51 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
holder.bubbleLayout.setLayoutParams(layoutParams);
+ if(message.getAppData() != null && holder.fileTransferLayout.getVisibility() != View.VISIBLE){
+ if(LinphoneUtils.isExtensionImage(message.getAppData())){
+ holder.fileExtensionLabel.setVisibility(View.GONE);
+ holder.fileNameLabel.setVisibility(View.GONE);
+ }else {
+ String extension = (LinphoneUtils.getExtensionFromFileName(message.getAppData()));
+ if(extension != null)
+ extension = extension.toUpperCase();
+ else
+ extension = "FILE";
+
+ if (extension.length() > 4)
+ extension = extension.substring(0, 3);
+
+ //holder.messageImage.setImageResource(R.drawable.chat_attachment);
+ holder.fileExtensionLabel.setText(extension);
+ holder.fileExtensionLabel.setVisibility(View.VISIBLE);
+ //holder.fileExtensionLabel.setTag(message.getAppData());
+ holder.fileNameLabel.setText(LinphoneUtils.getNameFromFilePath(message.getAppData()));
+ holder.fileNameLabel.setVisibility(View.VISIBLE);
+ holder.fileExtensionLabel.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ File file = null;
+ Uri contentUri = null;
+ String imageUri = (String)holder.messageImage.getTag();
+ if (imageUri.startsWith("file://")) {
+ imageUri = imageUri.substring("file://".length());
+ file = new File(imageUri);
+ contentUri = FileProvider.getUriForFile(getActivity(), "org.linphone.provider", file);
+ } else if (imageUri.startsWith("content://")) {
+ contentUri = Uri.parse(imageUri);
+ } else {
+ file = new File(imageUri);
+ contentUri = FileProvider.getUriForFile(getActivity(), "org.linphone.provider", file);
+ }
+ intent.setDataAndType(contentUri, "*/*");
+ intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ context.startActivity(intent);
+ }
+ });
+ }
+ }
+
if (isEditMode) {
holder.delete.setVisibility(View.VISIBLE);
holder.delete.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@@ -1635,7 +1690,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
if(LinphoneUtils.isExtensionImage(path))
defaultBitmap = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.chat_picture_over);
else
- defaultBitmap = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.chat_attachment_over);
+ defaultBitmap = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.chat_attachment);
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncBitmap asyncBitmap = new AsyncBitmap(context.getResources(), defaultBitmap, task);
@@ -1660,7 +1715,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
path = params[0];
Bitmap bm = null;
Bitmap thumbnail = null;
-
if(LinphoneUtils.isExtensionImage(path)) {
if (path.startsWith("content")) {
try {
@@ -1723,17 +1777,17 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
-
+ File file = null;
Uri contentUri = null;
String imageUri = (String)v.getTag();
if (imageUri.startsWith("file://")) {
imageUri = imageUri.substring("file://".length());
- File file = new File(imageUri);
+ file = new File(imageUri);
contentUri = FileProvider.getUriForFile(getActivity(), "org.linphone.provider", file);
} else if (imageUri.startsWith("content://")) {
contentUri = Uri.parse(imageUri);
} else {
- File file = new File(imageUri);
+ file = new File(imageUri);
contentUri = FileProvider.getUriForFile(getActivity(), "org.linphone.provider", file);
}
intent.setDataAndType(contentUri, "*/*");
diff --git a/src/android/org/linphone/ContactsManager.java b/src/android/org/linphone/ContactsManager.java
index 7bd05b680..ee5830c3e 100644
--- a/src/android/org/linphone/ContactsManager.java
+++ b/src/android/org/linphone/ContactsManager.java
@@ -299,7 +299,6 @@ public class ContactsManager extends ContentObserver {
}
}
}
- Log.e(" =====>>>> ContacsManager - fetchContactsSync Ended");
}
long timeElapsed = (new Date()).getTime() - contactsTime.getTime();
diff --git a/src/android/org/linphone/LinphoneActivity.java b/src/android/org/linphone/LinphoneActivity.java
index b71406261..a1d3e0436 100644
--- a/src/android/org/linphone/LinphoneActivity.java
+++ b/src/android/org/linphone/LinphoneActivity.java
@@ -647,14 +647,12 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
return;
}
- Log.e(" ===>>> displayChat : message = "+message+" - fileUri = "+fileUri);
String pictureUri = null;
String thumbnailUri = null;
String displayName = null;
LinphoneAddress lAddress = null;
if(sipUri != null) {
- Log.e(" ===>>> displayChat : sipUri = "+ sipUri);
try {
lAddress = LinphoneManager.getLc().interpretUrl(sipUri);
} catch (LinphoneCoreException e) {
@@ -692,7 +690,6 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
changeCurrentFragment(FragmentsAvailable.CHAT, extras);
}
} else {
- Log.e(" ===>>> displayChat : currentFragment != Chat");
if(isTablet()){
changeCurrentFragment(FragmentsAvailable.CHAT_LIST, null);
//displayChat(sipUri, message, fileUri);
@@ -1326,7 +1323,6 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
} else {
if (!ContactsManager.getInstance().contactsFetchedOnce()) {
ContactsManager.getInstance().enableContactsAccess();
- Log.e(" ====>>>> LinphoneActivity - ContactsManager.getInstance().fetchContactsAsync() 2 !!!");
ContactsManager.getInstance().fetchContactsAsync();
}
}
diff --git a/src/android/org/linphone/LinphoneLauncherActivity.java b/src/android/org/linphone/LinphoneLauncherActivity.java
index e201eff4e..174e473f8 100644
--- a/src/android/org/linphone/LinphoneLauncherActivity.java
+++ b/src/android/org/linphone/LinphoneLauncherActivity.java
@@ -19,14 +19,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package org.linphone;
import android.app.Activity;
-import android.content.CursorLoader;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.provider.MediaStore;
import org.linphone.assistant.RemoteProvisioningActivity;
import org.linphone.mediastream.Log;
@@ -127,9 +124,13 @@ public class LinphoneLauncherActivity extends Activity {
stringFileShared = intent.getStringExtra(Intent.EXTRA_STREAM);
}else {
fileUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
- stringFileShared = getRealPathFromURI(fileUri);
+ stringFileShared = LinphoneUtils.getRealPathFromURI(getBaseContext(), fileUri);
if(stringFileShared == null)
- stringFileShared = fileUri.getPath();
+ if(fileUri.getPath().contains("/0/1/mediakey:/local"))
+ stringFileShared = LinphoneUtils.getFilePath(getBaseContext(), fileUri);
+ else
+ stringFileShared = fileUri.getPath();
+
}
newIntent.putExtra("fileShared", stringFileShared);
}
@@ -160,18 +161,7 @@ public class LinphoneLauncherActivity extends Activity {
}, 1000);
}
- public String getRealPathFromURI(Uri contentUri) {
- String[] proj = {MediaStore.Images.Media.DATA};
- CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null);
- Cursor cursor = loader.loadInBackground();
- if (cursor != null && cursor.moveToFirst()) {
- int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
- String result = cursor.getString(column_index);
- cursor.close();
- return result;
- }
- return null;
- }
+
private class ServiceWaitThread extends Thread {
public void run() {
diff --git a/src/android/org/linphone/LinphoneUtils.java b/src/android/org/linphone/LinphoneUtils.java
index 4b1e1efbd..6044426c3 100644
--- a/src/android/org/linphone/LinphoneUtils.java
+++ b/src/android/org/linphone/LinphoneUtils.java
@@ -23,8 +23,10 @@ import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.CursorLoader;
import android.content.Intent;
import android.content.res.Resources;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
@@ -32,9 +34,11 @@ import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
@@ -60,6 +64,8 @@ import org.linphone.mediastream.video.capture.hwconf.Hacks;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -73,6 +79,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.zip.ZipEntry;
@@ -472,6 +479,16 @@ public final class LinphoneUtils {
}
}
+ public static String getNameFromFilePath(String filePath) {
+ String name = filePath;
+ int i = filePath.lastIndexOf('/');
+ if (i > 0) {
+ name = filePath.substring(i+1);
+ }
+ return name;
+ }
+
+
public static String getExtensionFromFileName(String fileName) {
String extension = null;
int i = fileName.lastIndexOf('.');
@@ -713,5 +730,148 @@ public final class LinphoneUtils {
.show();
}
}
+
+
+ /************************************************************************************************
+ * Picasa/Photos management workaround *
+ ************************************************************************************************/
+
+ public static String getFilePath(final Context context, final Uri uri) {
+
+ // Google photo uri example
+ // content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2FAF1QipMObgoK_wDY66gu0QkMAi/ORIGINAL/NONE/114919
+
+ if ("content".equalsIgnoreCase(uri.getScheme())) {
+ String result = getDataColumn(context, uri, null, null); //
+ if (TextUtils.isEmpty(result))
+ if (uri.getAuthority().contains("com.google.android")) {
+ try {
+ File localFile = createImageFile(context, null);
+ FileInputStream remoteFile = getSourceStream(context, uri);
+ if(copyToFile(remoteFile, localFile))
+ result = localFile.getAbsolutePath();
+ remoteFile.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return result;
+ }
+ // File
+ else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Copy data from a source stream to destFile.
+ * Return true if succeed, return false if failed.
+ */
+ private static boolean copyToFile(InputStream inputStream, File destFile) {
+ if (inputStream == null || destFile == null) return false;
+ try {
+ OutputStream out = new FileOutputStream(destFile);
+ try {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ } finally {
+ out.close();
+ }
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ public static String getTimestamp() {
+ try {
+ return new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ROOT).format(new Date());
+ } catch (RuntimeException e) {
+ return new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
+ }
+ }
+
+ public static File createImageFile(Context context, String imageFileName) throws IOException {
+ if (TextUtils.isEmpty(imageFileName))
+ imageFileName = getTimestamp()+".JPEG"; // make random filename if you want.
+
+ final File root;
+ imageFileName = imageFileName;
+ root = context.getExternalCacheDir();
+
+ if (root != null && !root.exists())
+ root.mkdirs();
+ return new File(root, imageFileName);
+ }
+
+
+ public static FileInputStream getSourceStream(Context context, Uri u) throws FileNotFoundException {
+ FileInputStream out = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ ParcelFileDescriptor parcelFileDescriptor =
+ context.getContentResolver().openFileDescriptor(u, "r");
+ FileDescriptor fileDescriptor = null;
+ if (parcelFileDescriptor != null) {
+ fileDescriptor = parcelFileDescriptor.getFileDescriptor();
+ out = new FileInputStream(fileDescriptor);
+ }
+ } else {
+ out = (FileInputStream) context.getContentResolver().openInputStream(u);
+ }
+ return out;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {
+ column
+ };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ final int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+
+ return null;
+ }
+
+ public static String getRealPathFromURI(Context context, Uri contentUri) {
+ String[] proj = {MediaStore.Images.Media.DATA};
+ CursorLoader loader = new CursorLoader(context, contentUri, proj, null, null, null);
+ Cursor cursor = loader.loadInBackground();
+ if (cursor != null && cursor.moveToFirst()) {
+ int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ String result = cursor.getString(column_index);
+ cursor.close();
+ return result;
+ }
+ return null;
+ }
+
}