Added ability to set intelligence for tags from feed-reading view.

This commit is contained in:
RyanBateman 2012-08-30 17:05:33 -04:00
parent 20743cff9c
commit 78f89c9128
29 changed files with 254 additions and 16 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 B

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<ImageView
android:id="@+id/tag_negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="@drawable/classify_negative" />
<ImageView
android:id="@+id/tag_positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@drawable/classify_positive" />
<TextView
android:id="@+id/dialog_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_toLeftOf="@id/tag_positive"
android:layout_toRightOf="@id/tag_negative"
android:gravity="center_horizontal"
android:padding="10dp"
android:textSize="20dp" />
</RelativeLayout>

View file

@ -19,6 +19,8 @@
<string name="title_feed_search">Search for feeds</string>
<string name="add_feed_message">Add \"%s\" to your feeds?</string>
<string name="train_on">Train on \"%s\"</string>
<string name="dialog_title">Alert</string>
<string name="loading">Loading…</string>

View file

@ -50,7 +50,7 @@ public class FeedReading extends Reading {
feed = Feed.fromCursor(feedCursor);
setTitle(feed.title);
readingAdapter = new FeedReadingAdapter(getSupportFragmentManager(), feed, stories);
readingAdapter = new FeedReadingAdapter(getSupportFragmentManager(), feed, stories, classifier);
setupPager();

View file

@ -5,6 +5,7 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import com.newsblur.activity.ReadingAdapter;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Story;
import com.newsblur.fragment.LoadingFragment;
@ -13,10 +14,12 @@ import com.newsblur.fragment.ReadingItemFragment;
public class FeedReadingAdapter extends ReadingAdapter {
private final Feed feed;
private Classifier classifier;
public FeedReadingAdapter(FragmentManager fm, Feed feed, Cursor stories) {
public FeedReadingAdapter(FragmentManager fm, Feed feed, Cursor stories, Classifier classifier) {
super(fm, stories);
this.feed = feed;
this.classifier = classifier;
}
@Override
@ -26,7 +29,7 @@ public class FeedReadingAdapter extends ReadingAdapter {
return loadingFragment;
} else {
stories.moveToPosition(position);
return ReadingItemFragment.newInstance(Story.fromCursor(stories), feed.faviconColour, feed.faviconFade);
return ReadingItemFragment.newInstance(Story.fromCursor(stories), feed.faviconColour, feed.faviconFade, classifier);
}
}

View file

@ -192,7 +192,7 @@ public class MixedExpandableListAdapter extends BaseExpandableListAdapter{
@Override
public Cursor getGroup(int groupPosition) {
if (groupPosition >= blogCursorHelper.getCount()) {
return folderCursorHelper.moveTo(groupPosition - blogCursorHelper.getCount());
return folderCursorHelper.moveTo(groupPosition - blogCursorHelper.getCount() - 1);
} else {
return blogCursorHelper.moveTo(groupPosition);
}

View file

@ -27,7 +27,7 @@ public class MixedFeedsReadingAdapter extends ReadingAdapter {
stories.moveToPosition(position);
String feedFaviconColor = stories.getString(stories.getColumnIndex(DatabaseConstants.FEED_FAVICON_COLOUR));
String feedFaviconFade = stories.getString(stories.getColumnIndex(DatabaseConstants.FEED_FAVICON_FADE));
return ReadingItemFragment.newInstance(Story.fromCursor(stories), feedFaviconColor, feedFaviconFade);
return ReadingItemFragment.newInstance(Story.fromCursor(stories), feedFaviconColor, feedFaviconFade, null);
}
}

View file

@ -1,5 +1,6 @@
package com.newsblur.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -10,9 +11,11 @@ import android.database.Cursor;
import com.google.gson.annotations.SerializedName;
import com.newsblur.database.DatabaseConstants;
public class Classifier {
public class Classifier implements Serializable {
private static final long serialVersionUID = 8958319817246110753L;
public static final int AUTHOR = 0, FEED = 1, TITLE = 2, TAG = 3;
public static final int LIKE = 1, DISLIKE = -1, CLEAR_DISLIKE = 3, CLEAR_LIKE = 4;
@SerializedName("authors")
public HashMap<String, Integer> authors = new HashMap<String, Integer>();

View file

@ -0,0 +1,130 @@
package com.newsblur.fragment;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.newsblur.R;
import com.newsblur.domain.Classifier;
import com.newsblur.network.APIManager;
public class ClassifierDialogFragment extends DialogFragment {
private static final String KEY = "key";
private static final String FEED_ID = "feed_id";
private static final String TYPE = "type";
private static final String CLASSIFIER = "classifier";
private String key, feedId;
private Classifier classifier;
private int classifierType;
private APIManager apiManager;
public static ClassifierDialogFragment newInstance(final String feedId, final Classifier classifier, final String key, final int classifierType) {
ClassifierDialogFragment frag = new ClassifierDialogFragment();
Bundle args = new Bundle();
args.putString(KEY, key);
args.putString(FEED_ID, feedId);
args.putInt(TYPE, classifierType);
args.putSerializable(CLASSIFIER, classifier);
frag.setArguments(args);
return frag;
}
@Override
public void onCreate(Bundle savedInstanceState) {
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.dialog);
feedId = getArguments().getString(FEED_ID);
key = getArguments().getString(KEY);
classifierType = getArguments().getInt(TYPE);
classifier = (Classifier) getArguments().getSerializable(CLASSIFIER);
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
apiManager = new APIManager(getActivity());
View v = inflater.inflate(R.layout.fragment_classify_dialog, container, false);
final TextView message = (TextView) v.findViewById(R.id.dialog_message);
message.setText(key);
final ImageView classifyPositive = (ImageView) v.findViewById(R.id.tag_positive);
final ImageView classifyNegative = (ImageView) v.findViewById(R.id.tag_negative);
if (classifier.tags.containsKey(key)) {
switch (classifier.tags.get(key)) {
case Classifier.LIKE:
classifyPositive.setImageResource(R.drawable.tag_positive_already);
break;
case Classifier.DISLIKE:
classifyNegative.setImageResource(R.drawable.tag_negative_already);
break;
}
}
classifyNegative.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... arg0) {
if (classifier.tags.containsKey(key) && classifier.tags.get(key) == Classifier.DISLIKE) {
return apiManager.trainClassifier(feedId, key, Classifier.TAG, Classifier.CLEAR_DISLIKE);
} else {
return apiManager.trainClassifier(feedId, key, Classifier.TAG, Classifier.DISLIKE);
}
}
@Override
protected void onPostExecute(Boolean result) {
if (result.booleanValue()) {
Toast.makeText(getActivity(), "Classifier saved", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), "Error saving classifier", Toast.LENGTH_SHORT).show();
}
ClassifierDialogFragment.this.dismiss();
};
}.execute();
}
});
classifyPositive.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... arg0) {
if (classifier.tags.containsKey(key) && classifier.tags.get(key) == Classifier.LIKE) {
return apiManager.trainClassifier(feedId, key, Classifier.TAG, Classifier.CLEAR_LIKE);
} else {
return apiManager.trainClassifier(feedId, key, Classifier.TAG, Classifier.LIKE);
}
}
@Override
protected void onPostExecute(Boolean result) {
if (result.booleanValue()) {
Toast.makeText(getActivity(), "Classifier saved", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), "Error saving classifier", Toast.LENGTH_SHORT).show();
}
ClassifierDialogFragment.this.dismiss();
}
}.execute();
}
});
return v;
}
}

View file

@ -1,13 +1,18 @@
package com.newsblur.fragment;
import java.lang.ref.WeakReference;
import android.content.ContentResolver;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayout;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
@ -15,6 +20,7 @@ import android.widget.TextView;
import com.newsblur.R;
import com.newsblur.activity.NewsBlurApplication;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Story;
import com.newsblur.network.APIManager;
import com.newsblur.network.SetupCommentSectionTask;
@ -28,16 +34,18 @@ public class ReadingItemFragment extends Fragment {
private APIManager apiManager;
private ImageLoader imageLoader;
private String feedColor;
private Classifier classifier;
private String feedFade;
private ContentResolver resolver;
public static ReadingItemFragment newInstance(Story story, String feedFaviconColor, String feedFaviconFade) {
public static ReadingItemFragment newInstance(Story story, String feedFaviconColor, String feedFaviconFade, Classifier classifier) {
ReadingItemFragment readingFragment = new ReadingItemFragment();
Bundle args = new Bundle();
args.putSerializable("story", story);
args.putString("feedColor", feedFaviconColor);
args.putString("feedFade", feedFaviconFade);
args.putSerializable("classifier", classifier);
readingFragment.setArguments(args);
return readingFragment;
@ -56,6 +64,8 @@ public class ReadingItemFragment extends Fragment {
feedColor = getArguments().getString("feedColor");
feedFade = getArguments().getString("feedFade");
classifier = (Classifier) getArguments().getSerializable("classifier");
}
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
@ -63,7 +73,6 @@ public class ReadingItemFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_readingitem, null);
WebView web = (WebView) view.findViewById(R.id.reading_webview);
setupWebview(web);
setupItemMetadata(view);
@ -94,7 +103,7 @@ public class ReadingItemFragment extends Fragment {
}
View sidebar = view.findViewById(R.id.row_item_sidebar);
if (story.getIntelligenceTotal() > 0) {
sidebar.setBackgroundResource(R.drawable.positive_count_circle);
} else if (story.getIntelligenceTotal() == 0) {
@ -111,16 +120,30 @@ public class ReadingItemFragment extends Fragment {
itemTitle.setText(story.title);
itemAuthors.setText(story.authors);
setupTags(view);
}
private void setupTags(View view) {
GridLayout tagContainer = (GridLayout) view.findViewById(R.id.reading_item_tags);
if (story.tags != null || story.tags.length > 0) {
tagContainer.setVisibility(View.VISIBLE);
for (String tag : story.tags) {
for (final String tag : story.tags) {
View v = inflater.inflate(R.layout.tag_view, null);
GridLayout.LayoutParams params = new GridLayout.LayoutParams();
params.columnSpec = GridLayout.spec(1, 1);
TextView tagText = (TextView) v.findViewById(R.id.tag_text);
tagText.setText(tag);
v.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
ClassifierDialogFragment classifierFragment = ClassifierDialogFragment.newInstance(story.feedId, classifier, tag, Classifier.TAG);
classifierFragment.show(ReadingItemFragment.this.getFragmentManager(), "dialog");
}
});
tagContainer.addView(v);
}
}

View file

@ -28,6 +28,8 @@ public class APIConstants {
public static final String URL_REPLY_TO = "http://www.newsblur.com/social/save_comment_reply";
public static final String URL_ADD_FEED = "http://www.newsblur.com/reader/add_url";
public static final String URL_CLASSIFIER_SAVE = "http://www.newsblur.com/classifier/save";
public static final String PARAMETER_FEEDS = "feeds";
public static final String PARAMETER_PASSWORD = "password";
public static final String PARAMETER_USER_ID = "user_id";
@ -50,10 +52,4 @@ public class APIConstants {
public static final String NEWSBLUR_URL = "http://www.newsblur.com";
}

View file

@ -21,6 +21,7 @@ import com.google.gson.reflect.TypeToken;
import com.newsblur.R;
import com.newsblur.database.DatabaseConstants;
import com.newsblur.database.FeedProvider;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Comment;
import com.newsblur.domain.Feed;
import com.newsblur.domain.FeedResult;
@ -333,6 +334,53 @@ public class APIManager {
}
}
}
public boolean trainClassifier(String feedId, String key, int type, int action) {
String typeText = null;
String actionText = null;
switch (type) {
case Classifier.AUTHOR:
typeText = "author";
break;
case Classifier.TAG:
typeText = "tag";
break;
case Classifier.TITLE:
typeText = "title";
break;
case Classifier.FEED:
typeText = "feed";
break;
}
switch (action) {
case Classifier.CLEAR_LIKE:
actionText = "remove_like_";
break;
case Classifier.CLEAR_DISLIKE:
actionText = "remove_dislike_";
break;
case Classifier.LIKE:
actionText = "like_";
break;
case Classifier.DISLIKE:
actionText = "dislike_";
break;
}
StringBuilder builder = new StringBuilder();;
builder.append(actionText);
builder.append(typeText);
ContentValues values = new ContentValues();
values.put(builder.toString(), key);
values.put(APIConstants.PARAMETER_FEEDID, feedId);
final APIClient client = new APIClient(context);
final APIResponse response = client.post(APIConstants.URL_CLASSIFIER_SAVE, values);
return (response.responseCode == HttpStatus.SC_OK && !response.hasRedirected);
}
public ProfileResponse getUser(String userId) {
final APIClient client = new APIClient(context);