Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions shaky/src/main/java/com/linkedin/android/shaky/AttachmentData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.linkedin.android.shaky;

import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

class AttachmentData {

final Uri uri;
final String displayName;
final boolean removable;

AttachmentData(@NonNull Uri uri) {
this(uri, null, false);
}

AttachmentData(@NonNull Uri uri, @Nullable String displayName, boolean removable) {
this.uri = uri;
this.displayName = displayName;
this.removable = removable;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

AttachmentData that = (AttachmentData) o;

return uri.equals(that.uri);
}

@Override
public int hashCode() {
return uri.hashCode();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.linkedin.android.shaky;

import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;

import java.util.ArrayList;

class AttachmentListAdapter extends ArrayAdapter<AttachmentData> {

private final AttachmentViewHolder.OnAttachmentClickListener onAttachmentClickListener;

AttachmentListAdapter(@NonNull Context context,
@NonNull AttachmentViewHolder.OnAttachmentClickListener onAttachmentClickListener) {
super(context, 0);
this.onAttachmentClickListener = onAttachmentClickListener;
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.shaky_attachment_view, parent, false);
AttachmentViewHolder viewHolder = new AttachmentViewHolder(convertView, onAttachmentClickListener);
convertView.setTag(viewHolder);
}
AttachmentViewHolder viewHolder = (AttachmentViewHolder) convertView.getTag();
if (viewHolder != null) {
viewHolder.bind(getItem(position));
}
return convertView;
}

boolean containsAttachmentUri(@NonNull Uri uri) {
int size = getCount();
AttachmentData data;
for (int i = 1; i < size; i++) {
data = getItem(i);
if (data != null && uri.equals(data.uri)) {
return true;
}
}
return false;
}

ArrayList<Uri> getAttachmentUriList() {
int size = getCount();
AttachmentData data;
ArrayList<Uri> list = new ArrayList<>(size);
// Do not add screenshot
for (int i = 1; i < size; i++) {
data = getItem(i);
if (data != null) {
list.add(data.uri);
}
}
return list;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.linkedin.android.shaky;

import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Html;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

class AttachmentViewHolder {

interface OnAttachmentClickListener {

void onRemoved(@NonNull Uri fileUri);

void onClicked(@NonNull Uri fileUri);
}

private static final String IMAGE_PREFIX = "image/";
private final TextView filenameView;
private final ImageView thumbnailView;
private final ImageView deleteIcon;
private final View rootView;

AttachmentViewHolder(@NonNull View view, @NonNull final OnAttachmentClickListener listener) {
filenameView = (TextView) view.findViewById(R.id.filename_view);
deleteIcon = (ImageView) view.findViewById(R.id.delete_icon);
thumbnailView = (ImageView) view.findViewById(R.id.thumbnail_view);
rootView = view;
filenameView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v.getTag() instanceof Uri) {
listener.onClicked((Uri) v.getTag());
}
}
});
thumbnailView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v.getTag() instanceof Uri) {
listener.onClicked((Uri) v.getTag());
}
}
});
deleteIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v.getTag() instanceof Uri) {
listener.onRemoved((Uri) v.getTag());
}
}
});
}

private void bind(@NonNull Uri fileUri, @Nullable String defaultFilename, boolean removable) {
filenameView.setText(Html.fromHtml(getFilename(fileUri, defaultFilename)));
deleteIcon.setVisibility(removable ? View.VISIBLE : View.GONE);
filenameView.setTag(fileUri);
deleteIcon.setTag(fileUri);
thumbnailView.setTag(fileUri);
String mimeType = Utils.getMimeType(rootView.getContext(), fileUri);
if (mimeType != null && mimeType.startsWith(IMAGE_PREFIX)) {
thumbnailView.setVisibility(View.VISIBLE);
thumbnailView.setImageURI(fileUri);
} else {
thumbnailView.setVisibility(View.GONE);
}
}

void bind(@Nullable AttachmentData item) {
if (item == null) {
// edge case
rootView.setVisibility(View.GONE);
} else {
rootView.setVisibility(View.VISIBLE);
bind(item.uri, item.displayName, item.removable);
}
}

private String getFilename(@NonNull Uri fileUri, @Nullable String defaultFilename) {
return TextUtils.isEmpty(defaultFilename) ? Utils.getFilename(rootView.getContext(), fileUri)
: defaultFilename;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
Expand All @@ -29,6 +30,8 @@
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;

import java.util.ArrayList;

/**
* The main activity used capture and send feedback.
*/
Expand All @@ -40,18 +43,27 @@ public class FeedbackActivity extends AppCompatActivity {
static final String MESSAGE = "message";
static final String TITLE = "title";
static final String USER_DATA = "userData";
static final String EXTRA_ATTACHMENTS = "extraAttachments";
static final String ADD_ATTACHMENT = "addAttachment";

private Uri imageUri;
private @FeedbackItem.FeedbackType int feedbackType;
private Bundle userData;
private boolean addAttachmentShown;

// Replace a fragment won't call fragment.onSaveInstanceState() so we need to have a activity scope states
// to maintain the fragment's states, such as attachments
final Bundle fragmentStates = new Bundle();

@NonNull
public static Intent newIntent(@NonNull Context context,
@Nullable Uri screenshotUri,
@Nullable Bundle userData) {
@Nullable Bundle userData,
boolean addAttachmentShown) {
Intent intent = new Intent(context, FeedbackActivity.class);
intent.putExtra(SCREENSHOT_URI, screenshotUri);
intent.putExtra(USER_DATA, userData);
intent.putExtra(ADD_ATTACHMENT, addAttachmentShown);
return intent;
}

Expand All @@ -63,7 +75,7 @@ public void onCreate(Bundle savedInstanceState) {

imageUri = getIntent().getParcelableExtra(SCREENSHOT_URI);
userData = getIntent().getBundleExtra(USER_DATA);

addAttachmentShown = getIntent().getBooleanExtra(ADD_ATTACHMENT, false);
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
Expand Down Expand Up @@ -110,7 +122,7 @@ private void changeToFragment(@NonNull Fragment fragment) {
private void startFormFragment(@FeedbackItem.FeedbackType int feedbackType) {
String title = getString(getTitleResId(feedbackType));
String hint = getString(getHintResId(feedbackType));
changeToFragment(FormFragment.newInstance(title, hint, imageUri));
changeToFragment(FormFragment.newInstance(title, hint, imageUri, addAttachmentShown));
}

/**
Expand Down Expand Up @@ -142,18 +154,23 @@ public void onReceive(Context context, Intent intent) {
} else if (DrawFragment.ACTION_DRAWING_COMPLETE.equals(intent.getAction())) {
onBackPressed();
} else if (FormFragment.ACTION_SUBMIT_FEEDBACK.equals(intent.getAction())) {
submitFeedbackIntent(intent.getStringExtra(FormFragment.EXTRA_USER_MESSAGE));
submitFeedbackIntent(intent.getStringExtra(FormFragment.EXTRA_USER_MESSAGE),
intent.getParcelableArrayListExtra(FormFragment.EXTRA_ATTACHMENTS));
}
}
};

private void submitFeedbackIntent(@Nullable String userMessage) {
private void submitFeedbackIntent(@Nullable String userMessage,
@Nullable ArrayList<Parcelable> attachments) {
Intent intent = new Intent(ACTION_END_FEEDBACK_FLOW);

intent.putExtra(SCREENSHOT_URI, imageUri);
intent.putExtra(TITLE, getString(getTitleResId(feedbackType)));
intent.putExtra(MESSAGE, userMessage);
intent.putExtra(USER_DATA, userData);
if (attachments != null && !attachments.isEmpty()) {
intent.putParcelableArrayListExtra(EXTRA_ATTACHMENTS, attachments);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we need to define EXTRA_ATTACHMENTS in both the fragment and the activity?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I just follow the same logic. When delegate tried to get the bundled values, it can use the constants defined in the same class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea makes sense, i guess these are two different semantic bundles. Sounds good!

}

LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
finish();
Expand Down
Loading