Skip to content
This repository was archived by the owner on Jul 25, 2024. It is now read-only.

Commit 5723078

Browse files
Sam1301niftynei
authored andcommitted
Register app as a way to share images
1 parent 961afaa commit 5723078

File tree

7 files changed

+360
-33
lines changed

7 files changed

+360
-33
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
88
<uses-permission android:name="android.permission.WAKE_LOCK" />
99
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
10+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
1011

1112
<permission
1213
android:name="${applicationId}.permission.C2D_MESSAGE"
@@ -32,6 +33,11 @@
3233

3334
<category android:name="android.intent.category.LAUNCHER" />
3435
</intent-filter>
36+
<intent-filter>
37+
<action android:name="android.intent.action.SEND" />
38+
<category android:name="android.intent.category.DEFAULT" />
39+
<data android:mimeType="image/*" />
40+
</intent-filter>
3541
</activity>
3642
<activity
3743
android:name=".activities.LoginActivity"

app/src/main/java/com/zulip/android/activities/ZulipActivity.java

Lines changed: 194 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
package com.zulip.android.activities;
22

3-
import java.sql.SQLException;
4-
import java.util.Arrays;
5-
import java.util.HashMap;
6-
import java.util.List;
7-
import java.util.concurrent.Callable;
8-
import java.util.ArrayList;
9-
import java.util.concurrent.TimeUnit;
10-
3+
import android.Manifest;
114
import android.animation.Animator;
125
import android.annotation.SuppressLint;
136
import android.annotation.TargetApi;
@@ -20,20 +13,23 @@
2013
import android.content.Intent;
2114
import android.content.IntentFilter;
2215
import android.content.SharedPreferences;
16+
import android.content.pm.PackageManager;
2317
import android.content.res.Configuration;
2418
import android.database.Cursor;
2519
import android.database.MatrixCursor;
2620
import android.database.MergeCursor;
2721
import android.graphics.Bitmap;
2822
import android.graphics.PorterDuff;
2923
import android.graphics.drawable.Drawable;
24+
import android.net.Uri;
3025
import android.os.Build;
3126
import android.os.Bundle;
3227
import android.os.CountDownTimer;
3328
import android.os.Handler;
3429
import android.support.design.widget.AppBarLayout;
3530
import android.support.design.widget.FloatingActionButton;
3631
import android.support.v4.app.ActionBarDrawerToggle;
32+
import android.support.v4.app.ActivityCompat;
3733
import android.support.v4.app.FragmentManager;
3834
import android.support.v4.app.FragmentTransaction;
3935
import android.support.v4.content.ContextCompat;
@@ -42,7 +38,6 @@
4238
import android.support.v4.view.animation.FastOutSlowInInterpolator;
4339
import android.support.v4.widget.DrawerLayout;
4440
import android.support.v4.widget.SimpleCursorAdapter;
45-
import android.support.v7.app.AppCompatActivity;
4641
import android.support.v7.app.AppCompatDelegate;
4742
import android.support.v7.widget.Toolbar;
4843
import android.text.TextUtils;
@@ -68,46 +63,53 @@
6863

6964
import com.j256.ormlite.android.AndroidDatabaseResults;
7065
import com.zulip.android.BuildConfig;
66+
import com.zulip.android.R;
67+
import com.zulip.android.ZulipApp;
7168
import com.zulip.android.database.DatabaseHelper;
72-
import com.zulip.android.models.Emoji;
73-
import com.zulip.android.filters.NarrowFilterToday;
74-
import com.zulip.android.models.Message;
75-
import com.zulip.android.models.MessageType;
7669
import com.zulip.android.filters.NarrowFilter;
7770
import com.zulip.android.filters.NarrowFilterAllPMs;
7871
import com.zulip.android.filters.NarrowFilterPM;
7972
import com.zulip.android.filters.NarrowFilterSearch;
8073
import com.zulip.android.filters.NarrowFilterStream;
74+
import com.zulip.android.filters.NarrowFilterToday;
8175
import com.zulip.android.filters.NarrowListener;
76+
import com.zulip.android.gcm.GcmBroadcastReceiver;
8277
import com.zulip.android.gcm.Notifications;
78+
import com.zulip.android.models.Emoji;
79+
import com.zulip.android.models.Message;
80+
import com.zulip.android.models.MessageType;
8381
import com.zulip.android.models.Person;
8482
import com.zulip.android.models.Presence;
8583
import com.zulip.android.models.PresenceType;
86-
import com.zulip.android.R;
8784
import com.zulip.android.models.Stream;
85+
import com.zulip.android.networking.AsyncGetEvents;
8886
import com.zulip.android.networking.AsyncSend;
89-
import com.zulip.android.networking.ZulipInterceptor;
90-
import com.zulip.android.networking.response.UserConfigurationResponse;
91-
import com.zulip.android.service.ZulipServices;
87+
import com.zulip.android.networking.AsyncStatusUpdate;
88+
import com.zulip.android.networking.ZulipAsyncPushTask;
89+
import com.zulip.android.networking.response.UploadResponse;
9290
import com.zulip.android.util.AnimationHelper;
91+
import com.zulip.android.util.FilePathHelper;
9392
import com.zulip.android.util.MutedTopics;
9493
import com.zulip.android.util.SwipeRemoveLinearLayout;
94+
import com.zulip.android.util.UrlHelper;
9595
import com.zulip.android.util.ZLog;
96-
import com.zulip.android.ZulipApp;
97-
import com.zulip.android.gcm.GcmBroadcastReceiver;
98-
import com.zulip.android.networking.AsyncGetEvents;
99-
import com.zulip.android.networking.AsyncStatusUpdate;
100-
import com.zulip.android.networking.ZulipAsyncPushTask;
10196

10297
import org.json.JSONObject;
10398

104-
import okhttp3.OkHttpClient;
105-
import okhttp3.Response;
106-
import okhttp3.ResponseBody;
99+
import java.io.File;
100+
import java.sql.SQLException;
101+
import java.util.ArrayList;
102+
import java.util.Arrays;
103+
import java.util.HashMap;
104+
import java.util.List;
105+
import java.util.concurrent.Callable;
106+
107+
import okhttp3.MediaType;
108+
import okhttp3.MultipartBody;
109+
import okhttp3.RequestBody;
107110
import retrofit2.Call;
108111
import retrofit2.Callback;
109-
import retrofit2.Retrofit;
110-
import retrofit2.converter.gson.GsonConverterFactory;
112+
import retrofit2.Response;
111113

112114
/**
113115
* The main Activity responsible for holding the {@link MessageListFragment} which has the list to the
@@ -122,6 +124,7 @@ public class ZulipActivity extends BaseActivity implements
122124
private static final int MAX_THRESOLD_EMOJI_HINT = 5;
123125
//At these many letters the emoji/person hint starts to show up
124126
private static final int MIN_THRESOLD_EMOJI_HINT = 1;
127+
private static final int PERMISSION_REQUEST_READ_CONTACTS = 1;
125128
private ZulipApp app;
126129
private List<Message> mutedTopics;
127130

@@ -178,6 +181,8 @@ public void onReceive(Context contenxt, Intent intent) {
178181
}
179182
};
180183
private ExpandableStreamDrawerAdapter streamsDrawerAdapter;
184+
private boolean mReadExternalStorage;
185+
private Uri mImageUri;
181186

182187
@Override
183188
public void removeChatBox(boolean animToRight) {
@@ -535,6 +540,158 @@ public Cursor runQuery(CharSequence charSequence) {
535540
}
536541
});
537542
messageEt.setAdapter(combinedAdapter);
543+
544+
// Get intent, action and MIME type
545+
Intent intent = getIntent();
546+
String action = intent.getAction();
547+
String type = intent.getType();
548+
549+
if (Intent.ACTION_SEND.equals(action) && type != null) {
550+
if (type.startsWith("image/")) {
551+
// Handle single image being sent
552+
handleSentImage(intent);
553+
}
554+
}
555+
}
556+
557+
@Override
558+
protected void onNewIntent(Intent intent) {
559+
super.onNewIntent(intent);
560+
561+
// Get action and MIME type of intent
562+
String action = intent.getAction();
563+
String type = intent.getType();
564+
565+
if (Intent.ACTION_SEND.equals(action) && type != null) {
566+
if (type.startsWith("image/")) {
567+
// Handle single image being sent
568+
handleSentImage(intent);
569+
}
570+
}
571+
}
572+
573+
/**
574+
* Function invoked when a user shares an image with the zulip app
575+
* @param intent passed to the activity with action SEND
576+
*/
577+
private void handleSentImage(Intent intent) {
578+
mImageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
579+
if (mImageUri != null) {
580+
// check if user has granted read external storage permission
581+
// for Android 6.0 or higher
582+
if (ContextCompat.checkSelfPermission(this,
583+
Manifest.permission.READ_EXTERNAL_STORAGE)
584+
!= PackageManager.PERMISSION_GRANTED) {
585+
// we need to request the permission.
586+
ActivityCompat.requestPermissions(this,
587+
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
588+
PERMISSION_REQUEST_READ_CONTACTS);
589+
} else {
590+
// permission already granted
591+
// start with file upload
592+
startFileUpload();
593+
}
594+
} else {
595+
Toast.makeText(this, R.string.cannot_find_image, Toast.LENGTH_SHORT).show();
596+
}
597+
}
598+
599+
@Override
600+
public void onRequestPermissionsResult(int requestCode,
601+
String permissions[], int[] grantResults) {
602+
603+
switch (requestCode) {
604+
case PERMISSION_REQUEST_READ_CONTACTS: {
605+
// If request is cancelled, the result arrays are empty.
606+
if (grantResults.length > 0
607+
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
608+
// permission granted
609+
// start with file upload
610+
startFileUpload();
611+
} else {
612+
// permission denied
613+
Toast.makeText(this, R.string.cannot_upload_image, Toast.LENGTH_SHORT).show();
614+
}
615+
}
616+
break;
617+
}
618+
}
619+
620+
/**
621+
* Helper function to update UI to indicate image is being uploaded and call
622+
* {@link ZulipActivity#uploadFile(String)} to upload the image.
623+
*/
624+
private void startFileUpload() {
625+
// Update UI to indicate image is being loaded
626+
// hide fab and display chatbox
627+
displayFAB(false);
628+
displayChatBox(true);
629+
String loadingMsg = getResources().getString(R.string.uploading_message);
630+
sendingMessage(true, loadingMsg);
631+
632+
// get actual file path
633+
String imageFilePath = FilePathHelper.getPath(this, mImageUri);
634+
635+
// upload the file asynchronously to the server
636+
uploadFile(imageFilePath);
637+
}
638+
639+
/**
640+
* Function to upload file asynchronously to the server using retrofit callback
641+
* upload {@link com.zulip.android.service.ZulipServices#upload(MultipartBody.Part)}
642+
* @param filePath on local storage
643+
*/
644+
private void uploadFile(String filePath) {
645+
File file = new File(filePath);
646+
647+
// create RequestBody instance from file
648+
RequestBody requestFile =
649+
RequestBody.create(MediaType.parse("multipart/form-data"), file);
650+
651+
// MultipartBody.Part is used to send also the actual file name
652+
MultipartBody.Part body =
653+
MultipartBody.Part.createFormData("picture", file.getName(), requestFile);
654+
655+
final String loadingMsg = getResources().getString(R.string.uploading_message);
656+
657+
// finally, execute the request
658+
// create upload service client
659+
Call<UploadResponse> call = ((ZulipApp) getApplicationContext()).getZulipServices().upload(body);
660+
call.enqueue(new Callback<UploadResponse>() {
661+
@Override
662+
public void onResponse(Call<UploadResponse> call,
663+
Response<UploadResponse> response) {
664+
if (response.isSuccessful()) {
665+
String filePathOnServer = "";
666+
UploadResponse uploadResponse = response.body();
667+
filePathOnServer = uploadResponse.getUri();
668+
if (!filePathOnServer.equals("")) {
669+
// remove loading message from the screen
670+
sendingMessage(false, loadingMsg);
671+
672+
// print message to compose box
673+
messageEt.append(" " + UrlHelper.addHost(filePathOnServer));
674+
} else {
675+
// remove loading message from the screen
676+
sendingMessage(false, loadingMsg);
677+
Toast.makeText(ZulipActivity.this, R.string.failed_to_upload, Toast.LENGTH_SHORT).show();
678+
}
679+
}
680+
else {
681+
// remove loading message from the screen
682+
sendingMessage(false, loadingMsg);
683+
Toast.makeText(ZulipActivity.this, R.string.failed_to_upload, Toast.LENGTH_SHORT).show();
684+
}
685+
686+
}
687+
688+
@Override
689+
public void onFailure(Call<UploadResponse> call, Throwable t) {
690+
// remove loading message from the screen
691+
sendingMessage(false, loadingMsg);
692+
ZLog.logException(t);
693+
}
694+
});
538695
}
539696

540697
/**
@@ -845,7 +1002,8 @@ private void sendMessage() {
8451002
messageEt.requestFocus();
8461003
return;
8471004
}
848-
sendingMessage(true);
1005+
final String sendingMsg = getResources().getString(R.string.sending_message);
1006+
sendingMessage(true, sendingMsg);
8491007
MessageType messageType = isCurrentModeStream() ? MessageType.STREAM_MESSAGE : MessageType.PRIVATE_MESSAGE;
8501008
Message msg = new Message(app);
8511009
msg.setSender(app.getYou());
@@ -864,13 +1022,13 @@ private void sendMessage() {
8641022
public void onTaskComplete(String result, JSONObject jsonObject) {
8651023
Toast.makeText(ZulipActivity.this, R.string.message_sent, Toast.LENGTH_SHORT).show();
8661024
messageEt.setText("");
867-
sendingMessage(false);
1025+
sendingMessage(false, sendingMsg);
8681026
}
8691027

8701028
public void onTaskFailure(String result) {
8711029
Log.d("onTaskFailure", "Result: " + result);
8721030
Toast.makeText(ZulipActivity.this, R.string.message_error, Toast.LENGTH_SHORT).show();
873-
sendingMessage(false);
1031+
sendingMessage(false, sendingMsg);
8741032
}
8751033
});
8761034
sender.execute();
@@ -879,15 +1037,18 @@ public void onTaskFailure(String result) {
8791037
/**
8801038
* Disable chatBox and show a loading footer while sending the message.
8811039
*/
882-
private void sendingMessage(boolean isSending) {
1040+
private void sendingMessage(boolean isSending, String message) {
8831041
streamActv.setEnabled(!isSending);
8841042
textView.setEnabled(!isSending);
8851043
messageEt.setEnabled(!isSending);
8861044
topicActv.setEnabled(!isSending);
8871045
sendBtn.setEnabled(!isSending);
8881046
togglePrivateStreamBtn.setEnabled(!isSending);
889-
if (isSending)
1047+
if (isSending) {
1048+
TextView msg = (TextView) composeStatus.findViewById(R.id.sending_message);
1049+
msg.setText(message);
8901050
composeStatus.setVisibility(View.VISIBLE);
1051+
}
8911052
else
8921053
composeStatus.setVisibility(View.GONE);
8931054
}
@@ -981,7 +1142,7 @@ public Cursor runQuery(CharSequence charSequence) {
9811142
}
9821143
});
9831144

984-
sendingMessage(false);
1145+
sendingMessage(false, getResources().getString(R.string.sending_message));
9851146
}
9861147

9871148
/**
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.zulip.android.networking.response;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
public class UploadResponse {
6+
7+
@SerializedName("uri")
8+
private String mUri;
9+
10+
public String getUri() {
11+
return mUri;
12+
}
13+
}

0 commit comments

Comments
 (0)