diff --git a/Chan/AndroidManifest.xml b/Chan/AndroidManifest.xml index fd66ca6d..71a1ab7e 100644 --- a/Chan/AndroidManifest.xml +++ b/Chan/AndroidManifest.xml @@ -51,6 +51,10 @@ android:name="org.floens.chan.activity.CatalogActivity" android:configChanges="keyboardHidden|orientation|screenSize" > + + - - + + + + + + + + + + + diff --git a/Chan/res/menu/base.xml b/Chan/res/menu/base.xml index b373f10b..22d499f8 100644 --- a/Chan/res/menu/base.xml +++ b/Chan/res/menu/base.xml @@ -1,15 +1,36 @@ + android:id="@+id/action_reload" + android:icon="@drawable/ic_action_refresh" + android:orderInCategory="1" + android:showAsAction="always" + android:title="@string/action_reload" /> + + + + + + diff --git a/Chan/res/values/dimens.xml b/Chan/res/values/dimens.xml index ed87407d..ba0ea4ac 100644 --- a/Chan/res/values/dimens.xml +++ b/Chan/res/values/dimens.xml @@ -1,7 +1,8 @@ 12dp - 11dp + 10dp + 11dp 6dp 70dp 24dp diff --git a/Chan/res/values/strings.xml b/Chan/res/values/strings.xml index c81a5e54..b67e0db7 100644 --- a/Chan/res/values/strings.xml +++ b/Chan/res/values/strings.xml @@ -1,6 +1,9 @@ + Cancel + Change + Chan Settings Reload @@ -8,6 +11,7 @@ Reply Open in browser Open catalog + Share General @@ -46,8 +50,10 @@ Close drawer Pinned threads + Enter title - Reply + Reply to + Make thread in Name Email Subject @@ -67,6 +73,8 @@ Ask before opening links Open link? + Default name + Default email Unsupported link Chan can\'t open this link. Opening it in your browser instead. diff --git a/Chan/res/values/styles.xml b/Chan/res/values/styles.xml index 10a4ea7d..a2bd440f 100644 --- a/Chan/res/values/styles.xml +++ b/Chan/res/values/styles.xml @@ -2,7 +2,6 @@ - - diff --git a/Chan/res/xml/preference.xml b/Chan/res/xml/preference.xml index 0ba36abd..1aae1eb8 100644 --- a/Chan/res/xml/preference.xml +++ b/Chan/res/xml/preference.xml @@ -16,6 +16,14 @@ android:key="preference_open_link_confirmation" android:defaultValue="true" /> + + + + parent, View view, int position, long id) { + Pin post = adapter.getItem(position); + if (post == null || post.type == Pin.Type.HEADER) return false; + + changePinTitle(post); + + return true; + } + }); + SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(drawerList, new DismissCallbacks() { @Override @@ -128,11 +149,23 @@ public abstract class BaseActivity extends Activity { return true; } + @Override + public void onPanelClosed(View view) { + } + + @Override + public void onPanelOpened(View view) { + } + + @Override + public void onPanelSlide(View view, float offset) { + } + /** - * Set the url that Android Beam will send to other users. + * Set the url that Android Beam and the share action will send. * @param url */ - public void setNfcPushUrl(String url) { + public void setShareUrl(String url) { NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); if (adapter != null) { @@ -147,6 +180,13 @@ public abstract class BaseActivity extends Activity { NdefMessage message = new NdefMessage(new NdefRecord[] {record}); adapter.setNdefPushMessage(message, this); } + + if (shareActionProvider != null) { + Intent share = new Intent(android.content.Intent.ACTION_SEND); + share.putExtra(android.content.Intent.EXTRA_TEXT, url); + share.setType("text/plain"); + shareActionProvider.setShareIntent(share); + } } /** @@ -175,6 +215,45 @@ public abstract class BaseActivity extends Activity { startActivity(data); } } + + private void changePinTitle(final Pin pin) { + final EditText text = new EditText(this); + text.setSingleLine(); + text.setText(pin.loadable.title); + text.setSelectAllOnFocus(true); + + AlertDialog dialog = new AlertDialog.Builder(this) + .setPositiveButton(R.string.change, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface d, int which) { + String value = text.getText().toString(); + + if (!TextUtils.isEmpty(value)) { + pin.loadable.title = value; + } + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface d, int which) { + } + }) + .setTitle(R.string.drawer_pinned_change_title) + .setView(text) + .create(); + + text.requestFocus(); + + dialog.setOnShowListener(new OnShowListener() { + @Override + public void onShow(DialogInterface dialog) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(text, 0); + } + }); + + dialog.show(); + } } diff --git a/Chan/src/org/floens/chan/activity/BoardActivity.java b/Chan/src/org/floens/chan/activity/BoardActivity.java index 843b768a..10300d60 100644 --- a/Chan/src/org/floens/chan/activity/BoardActivity.java +++ b/Chan/src/org/floens/chan/activity/BoardActivity.java @@ -6,7 +6,6 @@ import java.util.List; import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.entity.Loadable; -import org.floens.chan.entity.Loadable.Mode; import org.floens.chan.entity.Pin; import org.floens.chan.entity.Post; import org.floens.chan.fragment.ReplyFragment; @@ -23,11 +22,17 @@ import android.net.Uri; import android.os.Bundle; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.widget.DrawerLayout; +import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.widget.ArrayAdapter; +import android.widget.ShareActionProvider; public class BoardActivity extends BaseActivity implements ActionBar.OnNavigationListener { + private Loadable boardLoadable = new Loadable(); + private Loadable threadLoadable = new Loadable(); + private ThreadFragment boardFragment; private ThreadFragment threadFragment; private boolean boardSetByIntent = false; @@ -35,38 +40,41 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - loadable.mode = Loadable.Mode.BOARD; + boardLoadable.mode = Loadable.Mode.BOARD; + threadLoadable.mode = Loadable.Mode.THREAD; + boardFragment = ThreadFragment.newInstance(this); threadFragment = ThreadFragment.newInstance(this); + FragmentTransaction ft = getFragmentManager().beginTransaction(); - ft.replace(R.id.container, threadFragment); + ft.replace(R.id.left_pane, boardFragment); + ft.replace(R.id.right_pane, threadFragment); ft.commitAllowingStateLoss(); - final ActionBar actionBar = getActionBar(); - actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - actionBar.setHomeButtonEnabled(true); + updateActionBarState(); + final ActionBar actionBar = getActionBar(); actionBar.setListNavigationCallbacks( - new ArrayAdapter( - actionBar.getThemedContext(), - android.R.layout.simple_list_item_1, - android.R.id.text1, - ChanApplication.getBoardManager().getMyBoardsKeys() - ), this); + new ArrayAdapter( + actionBar.getThemedContext(), + R.layout.board_select_spinner, + android.R.id.text1, + ChanApplication.getBoardManager().getMyBoardsKeys() + ), this); Intent startIntent = getIntent(); Uri startUri = startIntent.getData(); if (savedInstanceState != null) { - loadable.readFromBundle(this, savedInstanceState); - loadable.no = 0; - loadable.listViewIndex = 0; - loadable.listViewTop = 0; - setNavigationFromBoardValue(loadable.board); + boardLoadable.readFromBundle(this, savedInstanceState); + boardLoadable.no = 0; + boardLoadable.listViewIndex = 0; + boardLoadable.listViewTop = 0; + setNavigationFromBoardValue(boardLoadable.board); } else if (startUri != null) { - handleStartURI(startUri); + handleIntentURI(startUri); } else { - actionBar.setSelectedNavigationItem(0); + getActionBar().setSelectedNavigationItem(0); } } @@ -74,7 +82,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - loadable.writeToBundle(this, outState); + boardLoadable.writeToBundle(this, outState); } @Override @@ -87,8 +95,8 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio @Override public boolean onNavigationItemSelected(int position, long id) { if (!boardSetByIntent) { - loadable = new Loadable(ChanApplication.getBoardManager().getMyBoardsValues().get(position)); - startLoading(loadable); + boardLoadable = new Loadable(ChanApplication.getBoardManager().getMyBoardsValues().get(position)); + startLoadingBoard(boardLoadable); } boardSetByIntent = false; @@ -97,98 +105,207 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio } @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (((ActionBarDrawerToggle) drawerListener).onOptionsItemSelected(item)) { - return true; - } - - switch(item.getItemId()) { - case R.id.action_catalog: - Loadable l = new Loadable(); - l.mode = Mode.CATALOG; - l.board = loadable.board; - startCatalogActivity(l); - - return true; - case R.id.action_reload: - threadFragment.reload(); - return true; - case R.id.action_reply: - ReplyFragment reply = ReplyFragment.newInstance(threadFragment); - reply.show(getFragmentManager(), "replyDialog"); - - return true; - case R.id.action_open_browser: - openUrl(ChanUrls.getBoardUrlDesktop(loadable.board)); - - return true; - } - - return super.onOptionsItemSelected(item); - } + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + shareActionProvider = (ShareActionProvider) menu.findItem(R.id.action_share).getActionProvider(); + + return true; + } - @Override + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - ((ActionBarDrawerToggle) drawerListener).onConfigurationChanged(newConfig); + drawerListener.onConfigurationChanged(newConfig); } @Override public void onDrawerClicked(Pin pin) { - startThreadActivity(pin.loadable); + startLoadingThread(pin.loadable); drawer.closeDrawer(drawerList); } @Override - public void onPostClicked(Post post) { - startThreadActivity(new Loadable(post.board, post.no, post.subject)); + public void onOPClicked(Post post) { + startLoadingThread(new Loadable(post.board, post.no, post.subject)); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - ((ActionBarDrawerToggle) drawerListener).syncState(); + drawerListener.syncState(); } - - private void startLoading(Loadable loadable) { - threadFragment.startLoading(loadable); - setNfcPushUrl(ChanUrls.getBoardUrlDesktop(loadable.board)); + + @Override + public void onBackPressed() { + if (pane.isOpen()) { + super.onBackPressed(); + } else { + pane.openPane(); + } } - private void startThreadActivity(Loadable loadable) { - Intent intent = new Intent(this, ThreadActivity.class); - - Bundle bundle = new Bundle(); - loadable.writeToBundle(this, bundle); + private void updateActionBarState() { + final ActionBar actionBar = getActionBar(); + + if (pane.isSlideable()) { + if (pane.isOpen()) { + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + actionBar.setHomeButtonEnabled(true); + actionBar.setTitle(""); + drawerListener.setDrawerIndicatorEnabled(true); + } else { + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + actionBar.setTitle(threadLoadable.title); + drawerListener.setDrawerIndicatorEnabled(false); + } + + actionBar.setDisplayHomeAsUpEnabled(true); + } else { + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + drawerListener.setDrawerIndicatorEnabled(true); + actionBar.setTitle(threadLoadable.title); + + actionBar.setDisplayHomeAsUpEnabled(false); + } + + actionBar.setDisplayShowTitleEnabled(true); - intent.putExtras(bundle); + invalidateOptionsMenu(); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + boolean open = pane.isOpen(); + + setMenuItemEnabled(menu.findItem(R.id.action_pin), !open); + + return super.onPrepareOptionsMenu(menu); + } + + private void setMenuItemEnabled(MenuItem item, boolean enabled) { + if (item != null) { + item.setVisible(enabled); + item.setEnabled(enabled); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (drawerListener.onOptionsItemSelected(item)) { + return true; + } + + switch(item.getItemId()) { + case R.id.action_reload: + if (pane.isOpen()) { + boardFragment.reload(); + } else { + if (threadFragment.getThreadManager().hasThread()) { + threadFragment.reload(); + } + } + return true; + case R.id.action_reply: + if (pane.isOpen()) { + startReply(pane.isSlideable(), boardFragment); + } else { + if (threadFragment.getThreadManager().hasThread()) { + startReply(pane.isSlideable(), threadFragment); + } + } + + return true; + case R.id.action_pin: + if (threadFragment.getThreadManager().hasThread()) { + Pin pin = new Pin(); + pin.loadable = threadLoadable; + + ChanApplication.getPinnedManager().add(pin); + + drawer.openDrawer(drawerList); + } + + return true; + case R.id.action_open_browser: + if (pane.isOpen()) { + openUrl(ChanUrls.getBoardUrlDesktop(boardLoadable.board)); + } else { + if (threadFragment.getThreadManager().hasThread()) { + openUrl(ChanUrls.getThreadUrlDesktop(threadLoadable.board, threadLoadable.no)); + } + } + + return true; + case android.R.id.home: + pane.openPane(); + + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void onPanelClosed(View view) { + updateActionBarState(); + } + + @Override + public void onPanelOpened(View view) { + updateActionBarState(); + } + + private void startReply(boolean inActivity, ThreadFragment fragment) { + if (inActivity) { + ReplyActivity.setThreadFragment(fragment); + Intent i = new Intent(this, ReplyActivity.class); + startActivity(i); + } else { + ReplyFragment reply = ReplyFragment.newInstance(fragment); + reply.show(getFragmentManager(), "replyDialog"); + } + } + + private void startLoadingBoard(Loadable loadable) { + this.boardLoadable = loadable; + + boardFragment.startLoading(loadable); + setShareUrl(ChanUrls.getBoardUrlDesktop(loadable.board)); - startActivity(intent); + updateActionBarState(); } - private void startCatalogActivity(Loadable loadable) { - Intent intent = new Intent(this, CatalogActivity.class); + private void startLoadingThread(Loadable loadable) { + Pin pin = ChanApplication.getPinnedManager().findPinByLoadable(loadable); + if (pin != null) { + // Use the loadable from the pin. + // This way we can store the listview position in the pin loadable, + // and not in a separate loadable instance. + loadable = pin.loadable; + } + + threadLoadable = loadable; - Bundle bundle = new Bundle(); - loadable.writeToBundle(this, bundle); + threadFragment.startLoading(loadable); + + setShareUrl(ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no)); + + if (TextUtils.isEmpty(loadable.title)) { + loadable.title = "/" + loadable.board + "/" + loadable.no; + } - intent.putExtras(bundle); + pane.closePane(); - startActivity(intent); + updateActionBarState(); } /** * Handle opening from an external url. * @param startUri */ - private void handleStartURI(Uri startUri) { + private void handleIntentURI(Uri startUri) { List parts = startUri.getPathSegments(); if (parts.size() == 1) { @@ -196,9 +313,9 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio String rawBoard = parts.get(0); if (ChanApplication.getBoardManager().getBoardExists(rawBoard)) { - loadable.board = rawBoard; + boardLoadable.board = rawBoard; boardSetByIntent = true; - startLoading(loadable); + startLoadingBoard(boardLoadable); ActionBar actionBar = getActionBar(); if (!setNavigationFromBoardValue(rawBoard)) { @@ -209,7 +326,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio } } else { - fallbackOpenUrl(startUri.toString()); + handleIntentURIFallback(startUri.toString()); } } else if (parts.size() == 3) { // Thread mode @@ -222,14 +339,14 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio } catch (NumberFormatException e) {} if (no >= 0 && ChanApplication.getBoardManager().getBoardExists(rawBoard)) { - loadable.board = rawBoard; + boardLoadable.board = rawBoard; boardSetByIntent = true; - startLoading(loadable); + startLoadingBoard(boardLoadable); - startThreadActivity(new Loadable(rawBoard, no)); + startLoadingThread(new Loadable(rawBoard, no)); finish(); } else { - fallbackOpenUrl(startUri.toString()); + handleIntentURIFallback(startUri.toString()); return; } } else { @@ -237,7 +354,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio } } - private void fallbackOpenUrl(final String url) { + private void handleIntentURIFallback(final String url) { new AlertDialog.Builder(this) .setTitle(R.string.open_unknown_title) .setMessage(R.string.open_unknown) diff --git a/Chan/src/org/floens/chan/activity/CatalogActivity.java b/Chan/src/org/floens/chan/activity/CatalogActivity.java index 9bb3539e..bf6418fb 100644 --- a/Chan/src/org/floens/chan/activity/CatalogActivity.java +++ b/Chan/src/org/floens/chan/activity/CatalogActivity.java @@ -8,17 +8,18 @@ import org.floens.chan.entity.Post; import org.floens.chan.fragment.ThreadFragment; import org.floens.chan.net.ChanUrls; -import android.app.FragmentTransaction; import android.os.Bundle; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; public class CatalogActivity extends BaseActivity { + private final Loadable loadable = new Loadable(); + private ThreadFragment threadFragment; @Override - public void onPostClicked(Post post) { + public void onOPClicked(Post post) { } @@ -34,9 +35,9 @@ public class CatalogActivity extends BaseActivity { loadable.mode = Mode.CATALOG; threadFragment = ThreadFragment.newInstance(this); - FragmentTransaction ft = getFragmentManager().beginTransaction(); + /*FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.container, threadFragment); - ft.commitAllowingStateLoss(); + ft.commitAllowingStateLoss();*/ Bundle bundle = getIntent().getExtras(); @@ -89,7 +90,7 @@ public class CatalogActivity extends BaseActivity { private void startLoading(Loadable loadable) { threadFragment.startLoading(loadable); - setNfcPushUrl(ChanUrls.getCatalogUrlDesktop(loadable.board)); + setShareUrl(ChanUrls.getCatalogUrlDesktop(loadable.board)); if (TextUtils.isEmpty(loadable.title)) { loadable.title = "Catalog /" + loadable.board + "/"; diff --git a/Chan/src/org/floens/chan/activity/ReplyActivity.java b/Chan/src/org/floens/chan/activity/ReplyActivity.java new file mode 100644 index 00000000..da63a357 --- /dev/null +++ b/Chan/src/org/floens/chan/activity/ReplyActivity.java @@ -0,0 +1,48 @@ +package org.floens.chan.activity; + +import org.floens.chan.fragment.ReplyFragment; +import org.floens.chan.fragment.ThreadFragment; + +import android.app.Activity; +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.util.Log; +import android.view.MenuItem; + +public class ReplyActivity extends Activity { + private static ThreadFragment threadFragment; + + public static void setThreadFragment(ThreadFragment tf) { + threadFragment = tf; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (threadFragment != null) { + getActionBar().setDisplayHomeAsUpEnabled(true); + + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(android.R.id.content, ReplyFragment.newInstance(threadFragment)); + ft.commitAllowingStateLoss(); + + threadFragment = null; + } else { + Log.e("Chan", "ThreadFragment was null, exiting!"); + finish(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case android.R.id.home: + finish(); + + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/Chan/src/org/floens/chan/activity/ThreadActivity.java b/Chan/src/org/floens/chan/activity/ThreadActivity.java index 9e3db4ca..304e68ac 100644 --- a/Chan/src/org/floens/chan/activity/ThreadActivity.java +++ b/Chan/src/org/floens/chan/activity/ThreadActivity.java @@ -1,30 +1,23 @@ package org.floens.chan.activity; -import java.util.ArrayList; - import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.entity.Loadable; import org.floens.chan.entity.Pin; import org.floens.chan.entity.Post; -import org.floens.chan.entity.PostLinkable; -import org.floens.chan.fragment.PostPopupFragment; import org.floens.chan.fragment.ReplyFragment; import org.floens.chan.fragment.ThreadFragment; import org.floens.chan.net.ChanUrls; -import android.app.AlertDialog; import android.app.FragmentTransaction; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceManager; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; public class ThreadActivity extends BaseActivity { + private Loadable loadable = new Loadable(); + private ThreadFragment threadFragment; @Override @@ -35,7 +28,7 @@ public class ThreadActivity extends BaseActivity { threadFragment = ThreadFragment.newInstance(this); FragmentTransaction ft = getFragmentManager().beginTransaction(); - ft.replace(R.id.container, threadFragment); + ft.replace(R.id.right_pane, threadFragment); ft.commitAllowingStateLoss(); Bundle bundle = getIntent().getExtras(); @@ -51,14 +44,6 @@ public class ThreadActivity extends BaseActivity { finish(); } - Pin pin = ChanApplication.getPinnedManager().findPinByLoadable(loadable); - if (pin != null) { - // Use the loadable from the pin. - // This way we can store the listview position in the pin loadable, - // and not in a separate loadable instance. - loadable = pin.loadable; - } - startLoading(loadable); } @@ -71,7 +56,7 @@ public class ThreadActivity extends BaseActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.thread, menu); +// getMenuInflater().inflate(R.menu.thread, menu); return super.onCreateOptionsMenu(menu); } @@ -118,43 +103,18 @@ public class ThreadActivity extends BaseActivity { startLoading(loadable); } - /** - * When the user clicks a post: - * a. when there's one linkable, open the linkable. - * b. when there's more than one linkable, show the user multiple options to select from. - * @param post The post that was clicked. - */ - @Override - public void onPostClicked(Post post) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - final ArrayList linkables = post.linkables; - - if (linkables.size() > 0) { - if (linkables.size() == 1) { - handleLinkableSelected(linkables.get(0)); - } else { - String[] keys = new String[linkables.size()]; - for (int i = 0; i < linkables.size(); i++) { - keys[i] = linkables.get(i).key; - } - - builder.setItems(keys, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - handleLinkableSelected(linkables.get(which)); - } - }); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - } - } - private void startLoading(Loadable loadable) { + Pin pin = ChanApplication.getPinnedManager().findPinByLoadable(loadable); + if (pin != null) { + // Use the loadable from the pin. + // This way we can store the listview position in the pin loadable, + // and not in a separate loadable instance. + loadable = pin.loadable; + } + threadFragment.startLoading(loadable); - setNfcPushUrl(ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no)); + setShareUrl(ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no)); if (TextUtils.isEmpty(loadable.title)) { loadable.title = "/" + loadable.board + "/" + loadable.no; @@ -162,75 +122,9 @@ public class ThreadActivity extends BaseActivity { getActionBar().setTitle(loadable.title); } - - /** - * Handle when a linkable has been clicked. - * @param linkable the selected linkable. - */ - private void handleLinkableSelected(final PostLinkable linkable) { - if (linkable.type == PostLinkable.Type.QUOTE) { - showPostPopup(linkable); - } else if (linkable.type == PostLinkable.Type.LINK) { - if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("preference_open_link_confirmation", true)) { - AlertDialog dialog = new AlertDialog.Builder(this) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - openLink(linkable); - } - }) - .setTitle(R.string.open_link_confirmation) - .setMessage(linkable.value) - .create(); - - dialog.show(); - } else { - openLink(linkable); - } - } - } - - /** - * When a linkable to a post has been clicked, - * show a dialog with the referenced post in it. - * @param linkable the clicked linkable. - */ - private void showPostPopup(PostLinkable linkable) { - String value = linkable.value; - - Post post = null; - - try { - // Get post id - String[] splitted = value.split("#p"); - if (splitted.length == 2) { - int id = Integer.parseInt(splitted[1]); - - post = threadFragment.getThreadManager().getPostById(id); - - if (post != null) { - PostPopupFragment popup = PostPopupFragment.newInstance(post, threadFragment.getThreadManager()); - - FragmentTransaction ft = getFragmentManager().beginTransaction(); - ft.add(popup, "postPopup"); - ft.commitAllowingStateLoss(); - } - } - } catch(NumberFormatException e) { - e.printStackTrace(); - } - } - - /** - * Open an url. - * @param linkable Linkable with an url. - */ - private void openLink(PostLinkable linkable) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(linkable.value))); - } + + @Override + public void onOPClicked(Post post) { + threadFragment.getThreadManager().showPostLinkables(post); + } } diff --git a/Chan/src/org/floens/chan/fragment/ReplyFragment.java b/Chan/src/org/floens/chan/fragment/ReplyFragment.java index 6ac1c485..8d8736e4 100644 --- a/Chan/src/org/floens/chan/fragment/ReplyFragment.java +++ b/Chan/src/org/floens/chan/fragment/ReplyFragment.java @@ -16,9 +16,11 @@ import org.floens.chan.utils.ViewFlipperAnimations; import android.app.Dialog; import android.app.DialogFragment; +import android.content.Context; import android.content.DialogInterface; import android.graphics.Bitmap; import android.os.Bundle; +import android.text.TextUtils; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -75,26 +77,24 @@ public class ReplyFragment extends DialogFragment { return reply; } - @Override - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - } - @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + if (threadManager == null) return; + getCaptcha(); } @Override - public void onDestroy() { - super.onDestroy(); - - ReplyManager replyManager = ReplyManager.getInstance(); - - // Save the info into the draft when existing - if (shouldSaveDraft) { + public void onPause() { + super.onPause(); + + if (threadManager == null) return; + + ReplyManager replyManager = ReplyManager.getInstance(); + + if (shouldSaveDraft) { draft.name = nameView.getText().toString(); draft.email = emailView.getText().toString(); draft.subject = subjectView.getText().toString(); @@ -105,18 +105,34 @@ public class ReplyFragment extends DialogFragment { replyManager.removeReplyDraft(); setFile(null); } - - replyManager.removeFileListener(); + + replyManager.removeFileListener(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + if (threadManager == null) { + closeReply(); + return new View(inflater.getContext()); + } + Dialog dialog = getDialog(); - dialog.setTitle(R.string.reply); + Loadable l = threadManager.getLoadable(); + Context context = inflater.getContext(); + String title = l.isThreadMode() ? + context.getString(R.string.reply) + " /" + l.board + "/" + l.no : + context.getString(R.string.reply_to_board) + " /" + l.board + "/"; + + if (dialog == null) { + getActivity().getActionBar().setTitle(title); + } else { + dialog.setTitle(title); + } + setClosable(true); // Setup the views with listeners - container = inflater.inflate(R.layout.reply_view, parent); + container = inflater.inflate(R.layout.reply_view, null); flipper = (ViewFlipper)container.findViewById(R.id.reply_flipper); nameView = (TextView)container.findViewById(R.id.reply_name); @@ -141,7 +157,7 @@ public class ReplyFragment extends DialogFragment { if (page == 1) { flipPage(0); } else { - dismiss(); + closeReply(); } } }); @@ -185,25 +201,35 @@ public class ReplyFragment extends DialogFragment { } }); - dialog.setOnKeyListener(new Dialog.OnKeyListener() { - @Override - public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - if (page == 1) { - flipPage(0); - } else if (page == 2) { - dismiss(); - } - - return true; - } else { - return false; - } - } - }); + if (dialog != null) { + dialog.setOnKeyListener(new Dialog.OnKeyListener() { + @Override + public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (page == 1) { + flipPage(0); + } else if (page == 2) { + closeReply(); + } + + return true; + } else { + return false; + } + } + }); + } Reply draft = ReplyManager.getInstance().getReplyDraft(); + if (TextUtils.isEmpty(draft.name)) { + draft.name = ChanApplication.getPreferences().getString("preference_default_name", ""); + } + + if (TextUtils.isEmpty(draft.email)) { + draft.email = ChanApplication.getPreferences().getString("preference_default_email", ""); + } + nameView.setText(draft.name); emailView.setText(draft.email); subjectView.setText(draft.subject); @@ -213,12 +239,22 @@ public class ReplyFragment extends DialogFragment { return container; } + private void closeReply() { + if (getDialog() != null) { + dismiss(); + } else { + getActivity().finish(); + } + } + /** * Set if the dialog is able to be closed, by pressing outside of the dialog, or something else. */ private void setClosable(boolean e) { - getDialog().setCanceledOnTouchOutside(e); - setCancelable(e); + if (getDialog() != null) { + getDialog().setCanceledOnTouchOutside(e); + setCancelable(e); + } } /** @@ -375,11 +411,12 @@ public class ReplyFragment extends DialogFragment { setClosable(true); flipPage(1); getCaptcha(); + captchaText.setText(""); } else if (response.isSuccessful) { shouldSaveDraft = false; Toast.makeText(getActivity(), R.string.reply_success, Toast.LENGTH_SHORT).show(); threadFragment.reload(); - dismiss(); + closeReply(); } else { if (isVisible()) { cancelButton.setEnabled(true); diff --git a/Chan/src/org/floens/chan/fragment/SettingsFragment.java b/Chan/src/org/floens/chan/fragment/SettingsFragment.java index d5f78cb1..7f0a381b 100644 --- a/Chan/src/org/floens/chan/fragment/SettingsFragment.java +++ b/Chan/src/org/floens/chan/fragment/SettingsFragment.java @@ -1,5 +1,6 @@ package org.floens.chan.fragment; +import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.activity.AboutActivity; @@ -10,7 +11,6 @@ import android.os.Bundle; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; import android.widget.Toast; public class SettingsFragment extends PreferenceFragment { @@ -42,7 +42,7 @@ public class SettingsFragment extends PreferenceFragment { if (++clickCount >= 5) { clickCount = 0; - SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getActivity()); + SharedPreferences p = ChanApplication.getPreferences(); boolean e = !p.getBoolean("preference_br", false); p.edit().putBoolean("preference_br", e).commit(); String m = e ? "Do a barrel roll" : "No barrel rolls this time"; diff --git a/Chan/src/org/floens/chan/fragment/ThreadFragment.java b/Chan/src/org/floens/chan/fragment/ThreadFragment.java index 6f9c3b5a..47524be9 100644 --- a/Chan/src/org/floens/chan/fragment/ThreadFragment.java +++ b/Chan/src/org/floens/chan/fragment/ThreadFragment.java @@ -33,6 +33,7 @@ public class ThreadFragment extends Fragment implements ThreadListener { private boolean shown = false; private Loadable loadable; private LoadView container; + private ListView listView; public static ThreadFragment newInstance(BaseActivity activity) { ThreadFragment fragment = new ThreadFragment(); @@ -70,6 +71,10 @@ public class ThreadFragment extends Fragment implements ThreadListener { if (container != null) { container.setView(null); } + + if (listView != null) { + listView.setOnScrollListener(null); + } } public void startLoading(Loadable loadable) { @@ -92,7 +97,7 @@ public class ThreadFragment extends Fragment implements ThreadListener { postAdapter = new PostAdapter(baseActivity, threadManager); - ListView listView = new ListView(baseActivity); + listView = new ListView(baseActivity); listView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); listView.setAdapter(postAdapter); listView.setSelectionFromTop(loadable.listViewIndex, loadable.listViewTop); @@ -104,9 +109,11 @@ public class ThreadFragment extends Fragment implements ThreadListener { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - loadable.listViewIndex = view.getFirstVisiblePosition(); - View v = view.getChildAt(0); - loadable.listViewTop = (v == null) ? 0 : v.getTop(); + if (loadable != null) { + loadable.listViewIndex = view.getFirstVisiblePosition(); + View v = view.getChildAt(0); + loadable.listViewTop = (v == null) ? 0 : v.getTop(); + } } }); } @@ -132,7 +139,11 @@ public class ThreadFragment extends Fragment implements ThreadListener { @Override public void onPostClicked(Post post) { - baseActivity.onPostClicked(post); + if (loadable.isThreadMode()) { + threadManager.showPostLinkables(post); + } else { + baseActivity.onOPClicked(post); + } } @Override diff --git a/Chan/src/org/floens/chan/manager/BoardManager.java b/Chan/src/org/floens/chan/manager/BoardManager.java index c0827701..640d55f6 100644 --- a/Chan/src/org/floens/chan/manager/BoardManager.java +++ b/Chan/src/org/floens/chan/manager/BoardManager.java @@ -11,7 +11,6 @@ import org.floens.chan.net.ChanUrls; import android.content.Context; import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; @@ -224,7 +223,7 @@ public class BoardManager { } private SharedPreferences getPreferences() { - return PreferenceManager.getDefaultSharedPreferences(context); + return ChanApplication.getPreferences(); } } diff --git a/Chan/src/org/floens/chan/manager/PinnedManager.java b/Chan/src/org/floens/chan/manager/PinnedManager.java index d15a61f2..d5636ada 100644 --- a/Chan/src/org/floens/chan/manager/PinnedManager.java +++ b/Chan/src/org/floens/chan/manager/PinnedManager.java @@ -3,13 +3,13 @@ package org.floens.chan.manager; import java.util.ArrayList; import java.util.Scanner; +import org.floens.chan.ChanApplication; import org.floens.chan.adapter.PinnedAdapter; import org.floens.chan.entity.Loadable; import org.floens.chan.entity.Pin; import android.content.Context; import android.content.SharedPreferences; -import android.preference.PreferenceManager; public class PinnedManager { private final ArrayList list = new ArrayList(); @@ -130,7 +130,7 @@ public class PinnedManager { } private SharedPreferences getPreferences() { - return PreferenceManager.getDefaultSharedPreferences(context); + return ChanApplication.getPreferences(); } } diff --git a/Chan/src/org/floens/chan/manager/ThreadManager.java b/Chan/src/org/floens/chan/manager/ThreadManager.java index fa10045c..cdf5523a 100644 --- a/Chan/src/org/floens/chan/manager/ThreadManager.java +++ b/Chan/src/org/floens/chan/manager/ThreadManager.java @@ -2,16 +2,23 @@ package org.floens.chan.manager; import java.util.ArrayList; +import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.entity.Loadable; import org.floens.chan.entity.Post; +import org.floens.chan.entity.PostLinkable; +import org.floens.chan.fragment.PostPopupFragment; import org.floens.chan.net.ThreadLoader; +import android.app.Activity; import android.app.AlertDialog; +import android.app.FragmentTransaction; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -29,15 +36,15 @@ import com.android.volley.VolleyError; * This manages some things like pages, starting and stopping of loading etc. */ public class ThreadManager { - private final Context context; + private final Activity activity; private final ThreadLoader threadLoader; private final ThreadManager.ThreadListener threadListener; private Loadable loadable; private boolean endOfLine = false; private final SparseArray postsById = new SparseArray(); - public ThreadManager(Context context, final ThreadListener listener) { - this.context = context; + public ThreadManager(Activity context, final ThreadListener listener) { + this.activity = context; threadListener = listener; threadLoader = new ThreadLoader(new ThreadLoader.ThreadLoaderListener() { @@ -57,6 +64,10 @@ public class ThreadManager { }); } + public boolean hasThread() { + return loadable != null; + } + public Post getPostById(int id) { return postsById.get(id); } @@ -112,7 +123,7 @@ public class ThreadManager { } public void onPostLongClicked(final Post post) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); + AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setItems(R.array.post_options, new DialogInterface.OnClickListener() { @Override @@ -142,14 +153,14 @@ public class ThreadManager { String errorMessage = ""; if ((error instanceof NoConnectionError) || (error instanceof NetworkError)) { - errorMessage = context.getString(R.string.thread_load_failed_network); + errorMessage = activity.getString(R.string.thread_load_failed_network); } else if (error instanceof ServerError) { - errorMessage = context.getString(R.string.thread_load_failed_server); + errorMessage = activity.getString(R.string.thread_load_failed_server); } else { - errorMessage = context.getString(R.string.thread_load_failed_parsing); + errorMessage = activity.getString(R.string.thread_load_failed_parsing); } - TextView view = new TextView(context); + TextView view = new TextView(activity); view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); view.setText(errorMessage); view.setTextSize(24f); @@ -159,7 +170,7 @@ public class ThreadManager { } private void copyText(String comment) { - ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("post text", comment); clipboard.setPrimaryClip(clip); } @@ -187,7 +198,7 @@ public class ThreadManager { text += "\nCountry: " + post.countryName; } - AlertDialog dialog = new AlertDialog.Builder(context) + AlertDialog dialog = new AlertDialog.Builder(activity) .setTitle(R.string.post_info) .setMessage(text) .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -200,6 +211,109 @@ public class ThreadManager { dialog.show(); } + /** + * When the user clicks a post: + * a. when there's one linkable, open the linkable. + * b. when there's more than one linkable, show the user multiple options to select from. + * @param post The post that was clicked. + */ + public void showPostLinkables(Post post) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + final ArrayList linkables = post.linkables; + + if (linkables.size() > 0) { + if (linkables.size() == 1) { + handleLinkableSelected(linkables.get(0)); + } else { + String[] keys = new String[linkables.size()]; + for (int i = 0; i < linkables.size(); i++) { + keys[i] = linkables.get(i).key; + } + + builder.setItems(keys, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + handleLinkableSelected(linkables.get(which)); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + } + } + + /** + * Handle when a linkable has been clicked. + * @param linkable the selected linkable. + */ + private void handleLinkableSelected(final PostLinkable linkable) { + if (linkable.type == PostLinkable.Type.QUOTE) { + showPostPopup(linkable); + } else if (linkable.type == PostLinkable.Type.LINK) { + if (ChanApplication.getPreferences().getBoolean("preference_open_link_confirmation", true)) { + AlertDialog dialog = new AlertDialog.Builder(activity) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + openLink(linkable); + } + }) + .setTitle(R.string.open_link_confirmation) + .setMessage(linkable.value) + .create(); + + dialog.show(); + } else { + openLink(linkable); + } + } + } + + /** + * When a linkable to a post has been clicked, + * show a dialog with the referenced post in it. + * @param linkable the clicked linkable. + */ + private void showPostPopup(PostLinkable linkable) { + String value = linkable.value; + + Post post = null; + + try { + // Get post id + String[] splitted = value.split("#p"); + if (splitted.length == 2) { + int id = Integer.parseInt(splitted[1]); + + post = getPostById(id); + + if (post != null) { + PostPopupFragment popup = PostPopupFragment.newInstance(post, this); + + FragmentTransaction ft = activity.getFragmentManager().beginTransaction(); + ft.add(popup, "postPopup"); + ft.commitAllowingStateLoss(); + } + } + } catch(NumberFormatException e) { + e.printStackTrace(); + } + } + + /** + * Open an url. + * @param linkable Linkable with an url. + */ + private void openLink(PostLinkable linkable) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(linkable.value))); + } + public interface ThreadListener { public void onThreadLoaded(ArrayList result); public void onThreadLoadError(VolleyError error); diff --git a/Chan/src/org/floens/chan/view/PostView.java b/Chan/src/org/floens/chan/view/PostView.java index b4987a3f..86d19f21 100644 --- a/Chan/src/org/floens/chan/view/PostView.java +++ b/Chan/src/org/floens/chan/view/PostView.java @@ -180,7 +180,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View isBuild = true; Resources resources = context.getResources(); - int postPadding = resources.getDimensionPixelSize(R.dimen.post_padding); + int commentPadding = resources.getDimensionPixelSize(R.dimen.post_comment_padding); int textPadding = resources.getDimensionPixelSize(R.dimen.post_text_padding); int iconWidth = resources.getDimensionPixelSize(R.dimen.post_icon_width); int iconHeight = resources.getDimensionPixelSize(R.dimen.post_icon_height); @@ -213,7 +213,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View LinearLayout right = new LinearLayout(context); right.setOrientation(VERTICAL); - right.setPadding(postPadding, postPadding, postPadding, postPadding); + right.setPadding(commentPadding, commentPadding, commentPadding, commentPadding); LinearLayout header = new LinearLayout(context); header.setOrientation(HORIZONTAL);