Partial account profile pages now in. Follows/Followers tabs are empty and block/follow does nothing yet.
parent
dbb2663882
commit
60d68b0ae6
@ -0,0 +1,267 @@ |
||||
/* Copyright 2017 Andrew Dawson |
||||
* |
||||
* This file is part of Tusky. |
||||
* |
||||
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU |
||||
* General Public License as published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even |
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
||||
* Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with Tusky. If not, see |
||||
* <http://www.gnu.org/licenses/>. */
|
||||
|
||||
package com.keylesspalace.tusky; |
||||
|
||||
import android.content.Context; |
||||
import android.content.Intent; |
||||
import android.content.SharedPreferences; |
||||
import android.os.Bundle; |
||||
import android.support.annotation.Nullable; |
||||
import android.support.design.widget.TabLayout; |
||||
import android.support.v4.view.ViewPager; |
||||
import android.support.v7.app.ActionBar; |
||||
import android.support.v7.app.AppCompatActivity; |
||||
import android.support.v7.widget.Toolbar; |
||||
import android.text.Spanned; |
||||
import android.view.Menu; |
||||
import android.view.MenuItem; |
||||
import android.widget.TextView; |
||||
|
||||
import com.android.volley.AuthFailureError; |
||||
import com.android.volley.Request; |
||||
import com.android.volley.Response; |
||||
import com.android.volley.VolleyError; |
||||
import com.android.volley.toolbox.ImageLoader; |
||||
import com.android.volley.toolbox.JsonArrayRequest; |
||||
import com.android.volley.toolbox.JsonObjectRequest; |
||||
import com.android.volley.toolbox.NetworkImageView; |
||||
|
||||
import org.json.JSONArray; |
||||
import org.json.JSONException; |
||||
import org.json.JSONObject; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
public class AccountActivity extends AppCompatActivity { |
||||
private String domain; |
||||
private String accessToken; |
||||
private boolean following = false; |
||||
private boolean blocking = false; |
||||
|
||||
@Override |
||||
protected void onCreate(@Nullable Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
setContentView(R.layout.activity_account); |
||||
|
||||
Intent intent = getIntent(); |
||||
String username = intent.getStringExtra("username"); |
||||
String id = intent.getStringExtra("id"); |
||||
TextView accountName = (TextView) findViewById(R.id.account_username); |
||||
accountName.setText(username); |
||||
|
||||
SharedPreferences preferences = getSharedPreferences( |
||||
getString(R.string.preferences_file_key), Context.MODE_PRIVATE); |
||||
domain = preferences.getString("domain", null); |
||||
accessToken = preferences.getString("accessToken", null); |
||||
assert(domain != null); |
||||
assert(accessToken != null); |
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); |
||||
setSupportActionBar(toolbar); |
||||
|
||||
NetworkImageView avatar = (NetworkImageView) findViewById(R.id.account_avatar); |
||||
NetworkImageView header = (NetworkImageView) findViewById(R.id.account_header); |
||||
avatar.setDefaultImageResId(R.drawable.avatar_default); |
||||
avatar.setErrorImageResId(R.drawable.avatar_error); |
||||
header.setDefaultImageResId(R.drawable.account_header_default); |
||||
|
||||
obtainAccount(id); |
||||
obtainRelationships(id); |
||||
|
||||
// Setup the tabs and timeline pager.
|
||||
AccountPagerAdapter adapter = new AccountPagerAdapter(getSupportFragmentManager(), id); |
||||
String[] pageTitles = { |
||||
getString(R.string.title_statuses), |
||||
getString(R.string.title_follows), |
||||
getString(R.string.title_followers) |
||||
}; |
||||
adapter.setPageTitles(pageTitles); |
||||
ViewPager viewPager = (ViewPager) findViewById(R.id.pager); |
||||
viewPager.setAdapter(adapter); |
||||
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout); |
||||
tabLayout.setupWithViewPager(viewPager); |
||||
} |
||||
|
||||
private void obtainAccount(String id) { |
||||
String endpoint = String.format(getString(R.string.endpoint_accounts), id); |
||||
String url = "https://" + domain + endpoint; |
||||
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, |
||||
new Response.Listener<JSONObject>() { |
||||
@Override |
||||
public void onResponse(JSONObject response) { |
||||
try { |
||||
onObtainAccountSuccess(response); |
||||
} catch (JSONException e) { |
||||
onObtainAccountFailure(); |
||||
} |
||||
} |
||||
}, |
||||
new Response.ErrorListener() { |
||||
@Override |
||||
public void onErrorResponse(VolleyError error) { |
||||
onObtainAccountFailure(); |
||||
} |
||||
}) { |
||||
@Override |
||||
public Map<String, String> getHeaders() throws AuthFailureError { |
||||
Map<String, String> headers = new HashMap<>(); |
||||
headers.put("Authorization", "Bearer " + accessToken); |
||||
return headers; |
||||
} |
||||
}; |
||||
VolleySingleton.getInstance(this).addToRequestQueue(request); |
||||
} |
||||
|
||||
private void onObtainAccountSuccess(JSONObject response) throws JSONException { |
||||
TextView username = (TextView) findViewById(R.id.account_username); |
||||
TextView displayName = (TextView) findViewById(R.id.account_display_name); |
||||
TextView note = (TextView) findViewById(R.id.account_note); |
||||
NetworkImageView avatar = (NetworkImageView) findViewById(R.id.account_avatar); |
||||
NetworkImageView header = (NetworkImageView) findViewById(R.id.account_header); |
||||
|
||||
String usernameFormatted = String.format( |
||||
getString(R.string.status_username_format), response.getString("acct")); |
||||
username.setText(usernameFormatted); |
||||
|
||||
String displayNameString = response.getString("display_name"); |
||||
displayName.setText(displayNameString); |
||||
ActionBar actionBar = getSupportActionBar(); |
||||
if (actionBar != null) { |
||||
actionBar.setTitle(displayNameString); |
||||
} |
||||
|
||||
String noteHtml = response.getString("note"); |
||||
Spanned noteSpanned = HtmlUtils.fromHtml(noteHtml); |
||||
note.setText(noteSpanned); |
||||
|
||||
ImageLoader imageLoader = VolleySingleton.getInstance(this).getImageLoader(); |
||||
avatar.setImageUrl(response.getString("avatar"), imageLoader); |
||||
String headerUrl = response.getString("header"); |
||||
if (!headerUrl.isEmpty() && !headerUrl.equals("/headers/original/missing.png")) { |
||||
header.setImageUrl(headerUrl, imageLoader); |
||||
} |
||||
} |
||||
|
||||
private void onObtainAccountFailure() { |
||||
//TODO: help
|
||||
assert(false); |
||||
} |
||||
|
||||
private void obtainRelationships(String id) { |
||||
String endpoint = getString(R.string.endpoint_relationships); |
||||
String url = String.format("https://%s%s?id=%s", domain, endpoint, id); |
||||
JsonArrayRequest request = new JsonArrayRequest(url, |
||||
new Response.Listener<JSONArray>() { |
||||
@Override |
||||
public void onResponse(JSONArray response) { |
||||
boolean following; |
||||
boolean blocking; |
||||
try { |
||||
JSONObject object = response.getJSONObject(0); |
||||
following = object.getBoolean("following"); |
||||
blocking = object.getBoolean("blocking"); |
||||
} catch (JSONException e) { |
||||
onObtainRelationshipsFailure(); |
||||
return; |
||||
} |
||||
onObtainRelationshipsSuccess(following, blocking); |
||||
} |
||||
}, |
||||
new Response.ErrorListener() { |
||||
@Override |
||||
public void onErrorResponse(VolleyError error) { |
||||
onObtainRelationshipsFailure(); |
||||
} |
||||
}) { |
||||
@Override |
||||
public Map<String, String> getHeaders() throws AuthFailureError { |
||||
Map<String, String> headers = new HashMap<>(); |
||||
headers.put("Authorization", "Bearer " + accessToken); |
||||
return headers; |
||||
} |
||||
}; |
||||
VolleySingleton.getInstance(this).addToRequestQueue(request); |
||||
} |
||||
|
||||
private void onObtainRelationshipsSuccess(boolean following, boolean blocking) { |
||||
this.following = following; |
||||
this.blocking = blocking; |
||||
if (!following || !blocking) { |
||||
invalidateOptionsMenu(); |
||||
} |
||||
} |
||||
|
||||
private void onObtainRelationshipsFailure() { |
||||
//TODO: help
|
||||
assert(false); |
||||
} |
||||
|
||||
@Override |
||||
public boolean onCreateOptionsMenu(Menu menu) { |
||||
getMenuInflater().inflate(R.menu.account_toolbar, menu); |
||||
return super.onCreateOptionsMenu(menu); |
||||
} |
||||
|
||||
@Override |
||||
public boolean onPrepareOptionsMenu(Menu menu) { |
||||
MenuItem follow = menu.findItem(R.id.action_follow); |
||||
String title; |
||||
if (following) { |
||||
title = getString(R.string.action_unfollow); |
||||
} else { |
||||
title = getString(R.string.action_follow); |
||||
} |
||||
follow.setTitle(title); |
||||
MenuItem block = menu.findItem(R.id.action_block); |
||||
if (blocking) { |
||||
title = getString(R.string.action_unblock); |
||||
} else { |
||||
title = getString(R.string.action_block); |
||||
} |
||||
block.setTitle(title); |
||||
return super.onPrepareOptionsMenu(menu); |
||||
} |
||||
|
||||
private void follow() { |
||||
|
||||
} |
||||
|
||||
private void block() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public boolean onOptionsItemSelected(MenuItem item) { |
||||
switch (item.getItemId()) { |
||||
case R.id.action_back: { |
||||
Intent intent = new Intent(this, MainActivity.class); |
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |
||||
startActivity(intent); |
||||
return true; |
||||
} |
||||
case R.id.action_follow: { |
||||
follow(); |
||||
return true; |
||||
} |
||||
case R.id.action_block: { |
||||
block(); |
||||
return true; |
||||
} |
||||
} |
||||
return super.onOptionsItemSelected(item); |
||||
} |
||||
} |
@ -0,0 +1,45 @@ |
||||
/* Copyright 2017 Andrew Dawson |
||||
* |
||||
* This file is part of Tusky. |
||||
* |
||||
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU |
||||
* General Public License as published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even |
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
||||
* Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with Tusky. If not, see |
||||
* <http://www.gnu.org/licenses/>. */
|
||||
|
||||
package com.keylesspalace.tusky; |
||||
|
||||
import android.os.Bundle; |
||||
import android.support.annotation.Nullable; |
||||
import android.support.v4.app.Fragment; |
||||
import android.view.LayoutInflater; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
|
||||
public class AccountFragment extends Fragment { |
||||
public enum Type { |
||||
FOLLOWS, |
||||
FOLLOWERS, |
||||
} |
||||
|
||||
public static AccountFragment newInstance(Type type) { |
||||
Bundle arguments = new Bundle(); |
||||
AccountFragment fragment = new AccountFragment(); |
||||
arguments.putString("type", type.name()); |
||||
fragment.setArguments(arguments); |
||||
return fragment; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, |
||||
@Nullable Bundle savedInstanceState) { |
||||
return super.onCreateView(inflater, container, savedInstanceState); |
||||
} |
||||
} |
@ -0,0 +1,62 @@ |
||||
/* Copyright 2017 Andrew Dawson |
||||
* |
||||
* This file is part of Tusky. |
||||
* |
||||
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU |
||||
* General Public License as published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even |
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
||||
* Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with Tusky. If not, see |
||||
* <http://www.gnu.org/licenses/>. */
|
||||
|
||||
package com.keylesspalace.tusky; |
||||
|
||||
import android.support.v4.app.Fragment; |
||||
import android.support.v4.app.FragmentManager; |
||||
import android.support.v4.app.FragmentPagerAdapter; |
||||
|
||||
public class AccountPagerAdapter extends FragmentPagerAdapter { |
||||
private String accountId; |
||||
private String[] pageTitles; |
||||
|
||||
public AccountPagerAdapter(FragmentManager manager, String accountId) { |
||||
super(manager); |
||||
this.accountId = accountId; |
||||
} |
||||
|
||||
public void setPageTitles(String[] titles) { |
||||
pageTitles = titles; |
||||
} |
||||
|
||||
@Override |
||||
public Fragment getItem(int position) { |
||||
switch (position) { |
||||
case 0: { |
||||
return TimelineFragment.newInstance(TimelineFragment.Kind.USER, accountId); |
||||
} |
||||
case 1: { |
||||
return AccountFragment.newInstance(AccountFragment.Type.FOLLOWS); |
||||
} |
||||
case 2: { |
||||
return AccountFragment.newInstance(AccountFragment.Type.FOLLOWERS); |
||||
} |
||||
default: { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int getCount() { |
||||
return 3; |
||||
} |
||||
|
||||
@Override |
||||
public CharSequence getPageTitle(int position) { |
||||
return pageTitles[position]; |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
/* Copyright 2017 Andrew Dawson |
||||
* |
||||
* This file is part of Tusky. |
||||
* |
||||
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU |
||||
* General Public License as published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even |
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
||||
* Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with Tusky. If not, see |
||||
* <http://www.gnu.org/licenses/>. */
|
||||
|
||||
package com.keylesspalace.tusky; |
||||
|
||||
import android.os.Build; |
||||
import android.text.Html; |
||||
import android.text.Spanned; |
||||
|
||||
public class HtmlUtils { |
||||
private static CharSequence trimTrailingWhitespace(CharSequence s) { |
||||
int i = s.length(); |
||||
do { |
||||
i--; |
||||
} while (i >= 0 && Character.isWhitespace(s.charAt(i))); |
||||
return s.subSequence(0, i + 1); |
||||
} |
||||
|
||||
public static Spanned fromHtml(String html) { |
||||
Spanned result; |
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
||||
result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); |
||||
} else { |
||||
result = Html.fromHtml(html); |
||||
} |
||||
/* Html.fromHtml returns trailing whitespace if the html ends in a </p> tag, which |
||||
* all status contents do, so it should be trimmed. */ |
||||
return (Spanned) trimTrailingWhitespace(result); |
||||
} |
||||
} |
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,122 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<android.support.design.widget.CoordinatorLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:orientation="vertical" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:fitsSystemWindows="true"> |
||||
|
||||
<android.support.design.widget.AppBarLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"> |
||||
|
||||
<android.support.design.widget.CollapsingToolbarLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed" |
||||
app:titleEnabled="false"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="vertical" |
||||
android:layout_marginTop="?attr/actionBarSize" |
||||
android:background="@color/account_header_background"> |
||||
|
||||
<com.android.volley.toolbox.NetworkImageView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:id="@+id/account_header" |
||||
android:scaleType="fitCenter" |
||||
android:adjustViewBounds="true" /> |
||||
|
||||
<RelativeLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"> |
||||
|
||||
<com.android.volley.toolbox.NetworkImageView |
||||
android:layout_width="80dp" |
||||
android:layout_height="80dp" |
||||
android:id="@+id/account_avatar" |
||||
android:paddingLeft="8dp" |
||||
android:paddingRight="8dp" |
||||
android:paddingTop="8dp" /> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="vertical" |
||||
android:layout_toRightOf="@id/account_avatar" |
||||
android:layout_centerVertical="true"> |
||||
|
||||
<TextView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:id="@+id/account_display_name" |
||||
android:textStyle="normal|bold" |
||||
android:textSize="18sp" /> |
||||
|
||||
<TextView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:id="@+id/account_username" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</RelativeLayout> |
||||
|
||||
<TextView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:id="@+id/account_note" |
||||
android:padding="8dp" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<android.support.v7.widget.Toolbar |
||||
android:id="@+id/toolbar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="?attr/actionBarSize" |
||||
android:background="?attr/colorPrimary" |
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" |
||||
android:layout_gravity="top" |
||||
app:layout_collapseMode="pin" /> |
||||
|
||||
</android.support.design.widget.CollapsingToolbarLayout> |
||||
|
||||
</android.support.design.widget.AppBarLayout> |
||||
|
||||
<android.support.v4.view.ViewPager |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:id="@+id/pager" |
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"> |
||||
|
||||
<android.support.design.widget.TabLayout |
||||
android:id="@+id/tab_layout" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"> |
||||
|
||||
<android.support.design.widget.TabItem |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" /> |
||||
|
||||
<android.support.design.widget.TabItem |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" /> |
||||
|
||||
<android.support.design.widget.TabItem |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" /> |
||||
|
||||
</android.support.design.widget.TabLayout> |
||||
|
||||
</android.support.v4.view.ViewPager> |
||||
|
||||
<FrameLayout |
||||
android:id="@+id/overlay_fragment_container" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" /> |
||||
|
||||
</android.support.design.widget.CoordinatorLayout> |
@ -0,0 +1,18 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto"> |
||||
|
||||
<item android:id="@+id/action_back" |
||||
android:title="@string/action_back" |
||||
android:icon="@drawable/ic_back" |
||||
app:showAsAction="always" /> |
||||
|
||||
<item android:id="@+id/action_follow" |
||||
android:title="@string/action_follow" |
||||
app:showAsAction="never" /> |
||||
|
||||
<item android:id="@+id/action_block" |
||||
android:title="@string/action_block" |
||||
app:showAsAction="never" /> |
||||
|
||||
</menu> |
Loading…
Reference in new issue