アルファっぽいの

This commit is contained in:
tateisu 2017-04-23 14:42:09 +09:00
parent ba568642e8
commit 8059878f86
57 changed files with 2394 additions and 586 deletions

View File

@ -2,6 +2,7 @@
<dictionary name="tateisu">
<words>
<w>dont</w>
<w>emoji</w>
<w>emojione</w>
<w>favourited</w>
<w>noto</w>
@ -10,6 +11,7 @@
<w>reblogged</w>
<w>reblogs</w>
<w>subwaytooter</w>
<w>swipy</w>
<w>timelines</w>
<w>unfavourite</w>
<w>unreblog</w>

View File

@ -1,14 +1,16 @@
import java.text.SimpleDateFormat
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion '25.0.0'
compileSdkVersion 25
buildToolsVersion '25.0.2'
defaultConfig {
applicationId "jp.juggler.subwaytooter"
minSdkVersion 21
targetSdkVersion 24
targetSdkVersion 25
versionCode 1
versionName "1.0"
versionName "0.0.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -17,23 +19,44 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
rc {
}
}
// Generate Signed APK
applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
// Rename APK
def versionCode = defaultConfig.versionCode
def versionName = defaultConfig.versionName
def flavor = variant.flavorName
def date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
def newName = "SubwayTooter-${flavor}-${versionCode}-${versionName}-${date}.apk"
output.outputFile = new File((String) output.outputFile.parent, (String) newName)
}
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:support-v4:24.2.0'
compile 'com.android.support:design:24.2.0'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:customtabs:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile 'com.android.volley:volley:1.0.0'
compile 'com.android.support:customtabs:24.2.0'
compile 'com.squareup.okhttp3:okhttp:3.7.0'
compile 'commons-io:commons-io:2.4'
compile 'uk.co.chrisjenx:calligraphy:2.2.0'
compile 'com.github.woxthebox:draglistview:1.4.3'
compile 'com.github.omadahealth:swipy:1.2.3@aar'
}

View File

@ -36,25 +36,24 @@
<activity
android:name=".ActAccountSetting"
android:label="@string/account_setting"
android:theme="@style/AppTheme.NoActionBar"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize"
/>
<activity
android:name=".ActAppSetting"
android:label="@string/app_setting"
android:theme="@style/AppTheme.NoActionBar"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize"
/>
<activity
android:name=".ActColumnList"
android:label="@string/column_list"
android:theme="@style/AppTheme.NoActionBar"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize"
/>
</application>
</manifest>

View File

@ -1,10 +1,14 @@
package jp.juggler.subwaytooter;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.os.AsyncTaskCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
@ -13,9 +17,14 @@ import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import jp.juggler.subwaytooter.api.TootApiClient;
import jp.juggler.subwaytooter.api.TootApiResult;
import jp.juggler.subwaytooter.api.entity.TootAccount;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.dialog.LoginForm;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class ActAccountSetting extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
static final LogCategory log = new LogCategory( "ActAccountSetting" );
@ -77,7 +86,6 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
swConfirmBeforeBoost.setChecked( a.confirm_boost );
swNSFWOpen.setChecked( a.dont_hide_nsfw );
updateVisibility();
}
@ -177,8 +185,82 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
///////////////////////////////////////////////////
private void performAccessToken(){
LoginForm.showLoginForm( this, account.host, new LoginForm.LoginFormCallback() {
@Override
public void startLogin( final Dialog dialog, final String instance, final String user_mail, final String password ){
final ProgressDialog progress = new ProgressDialog( ActAccountSetting.this );
final AsyncTask< Void, String, TootApiResult > task = new AsyncTask< Void, String, TootApiResult >() {
long row_id;
@Override
protected TootApiResult doInBackground( Void... params ){
TootApiClient api_client = new TootApiClient( ActAccountSetting.this, new TootApiClient.Callback() {
@Override
public boolean isApiCancelled(){
return isCancelled();
}
@Override
public void publishApiProgress( final String s ){
Utils.runOnMainThread( new Runnable() {
@Override
public void run(){
progress.setMessage( s );
}
} );
}
} );
api_client.setUserInfo( instance, user_mail, password );
TootApiResult result = api_client.request( "/api/v1/accounts/verify_credentials" );
if( result != null && result.object != null ){
TootAccount ta = TootAccount.parse( log, result.object );
if( ! ta.username.equals( account.username ) ){
return new TootApiResult( getString( R.string.user_name_not_match ) );
}
account.updateTokenInfo( result.token_info );
row_id = account.db_id;
}
return result;
}
@Override
protected void onPostExecute( TootApiResult result ){
progress.dismiss();
if( result == null ){
// cancelled.
}else if( result.object == null ){
Utils.showToast( ActAccountSetting.this, true, result.error );
log.e( result.error );
}else{
Utils.showToast( ActAccountSetting.this, false, R.string.access_token_updated );
dialog.dismiss();
}
}
};
progress.setIndeterminate( true );
progress.setCancelable( true );
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
@Override
public void onCancel( DialogInterface dialog ){
task.cancel( true );
}
} );
progress.show();
AsyncTaskCompat.executeParallel( task );
}
} );
}
///////////////////////////////////////////////////
}

View File

@ -1,10 +1,81 @@
package jp.juggler.subwaytooter;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.os.AsyncTaskCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
/**
* Created by tateisu on 2017/04/22.
*/
import jp.juggler.subwaytooter.util.LogCategory;
public class ActAppSetting extends AppCompatActivity {
public class ActAppSetting extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
static final LogCategory log = new LogCategory( "ActAppSetting" );
public static void open( Context context ){
context.startActivity( new Intent(context,ActAppSetting.class) );
}
SharedPreferences pref;
@Override
protected void onCreate( @Nullable Bundle savedInstanceState ){
super.onCreate( savedInstanceState );
initUI();
pref = Pref.pref(this);
loadUIFromData();
}
TextView tvInstance;
TextView tvUser;
View btnAccessToken;
View btnAccountRemove;
Button btnVisibility;
Switch swBackToColumnList;
Switch swDontConfirmBeforeCloseColumn;
private void initUI(){
setContentView( R.layout.act_app_setting );
swBackToColumnList = (Switch) findViewById( R.id.swBackToColumnList );
swDontConfirmBeforeCloseColumn = (Switch) findViewById( R.id.swDontConfirmBeforeCloseColumn );
swBackToColumnList.setOnCheckedChangeListener( this );
swDontConfirmBeforeCloseColumn.setOnCheckedChangeListener( this );
}
private void loadUIFromData( ){
swBackToColumnList.setChecked( pref.getBoolean( Pref.KEY_BACK_TO_COLUMN_LIST,false ) );
swDontConfirmBeforeCloseColumn.setChecked( pref.getBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN,false ) );
}
private void saveUIToData(){
pref
.edit()
.putBoolean( Pref.KEY_BACK_TO_COLUMN_LIST,swBackToColumnList.isChecked() )
.putBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN,swDontConfirmBeforeCloseColumn.isChecked() )
.apply();
}
@Override
public void onCheckedChanged( CompoundButton buttonView, boolean isChecked ){
saveUIToData();
}
}

View File

@ -1,10 +1,299 @@
package jp.juggler.subwaytooter;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* Created by tateisu on 2017/04/22.
*/
import com.woxthebox.draglistview.DragItem;
import com.woxthebox.draglistview.DragItemAdapter;
import com.woxthebox.draglistview.DragListView;
import com.woxthebox.draglistview.swipe.ListSwipeHelper;
import com.woxthebox.draglistview.swipe.ListSwipeItem;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import jp.juggler.subwaytooter.util.Utils;
public class ActColumnList extends AppCompatActivity {
public static final String EXTRA_ORDER = "order";
public static final String EXTRA_SELECTION = "selection";
@Override
protected void onCreate( @Nullable Bundle savedInstanceState ){
super.onCreate( savedInstanceState );
initUI();
if( savedInstanceState != null ){
restoreData(
savedInstanceState.getString( EXTRA_ORDER )
, savedInstanceState.getInt( EXTRA_SELECTION )
);
}else{
Intent intent = getIntent();
restoreData(
intent.getStringExtra( EXTRA_ORDER )
, intent.getIntExtra( EXTRA_SELECTION, - 1 )
);
}
}
@Override
protected void onSaveInstanceState( Bundle outState ){
super.onSaveInstanceState( outState );
//
outState.putInt( EXTRA_SELECTION, old_selection );
//
JSONArray array = new JSONArray();
List< MyItem > item_list = listAdapter.getItemList();
for( int i = 0, ie = item_list.size() ; i < ie ; ++ i ){
array.put( item_list.get( i ).json );
}
outState.putString( EXTRA_ORDER, array.toString() );
}
@Override
public void onBackPressed(){
makeResult( - 1 );
super.onBackPressed();
}
DragListView listView;
MyListAdapter listAdapter;
int old_selection;
private void initUI(){
setContentView( R.layout.act_column_list );
// リストのアダプター
listAdapter = new MyListAdapter();
// ハンドル部分をドラッグで並べ替えできるRecyclerView
listView = (DragListView) findViewById( R.id.drag_list_view );
listView.setLayoutManager( new LinearLayoutManager( this ) );
listView.setAdapter( listAdapter, true );
listView.setCanDragHorizontally( false );
listView.setCustomDragItem( new MyDragItem( this, R.layout.lv_column_list ) );
listView.getRecyclerView().setVerticalScrollBarEnabled( true );
listView.setDragListListener( new DragListView.DragListListenerAdapter() {
@Override
public void onItemDragStarted( int position ){
// 操作中はリフレッシュ禁止
// mRefreshLayout.setEnabled( false );
}
@Override
public void onItemDragEnded( int fromPosition, int toPosition ){
// 操作完了でリフレッシュ許可
// mRefreshLayout.setEnabled( USE_SWIPE_REFRESH );
// if( fromPosition != toPosition ){
// // 並べ替えが発生した
// }
}
} );
// リストを左右スワイプした
listView.setSwipeListener( new ListSwipeHelper.OnSwipeListenerAdapter() {
@Override
public void onItemSwipeStarted( ListSwipeItem item ){
// 操作中はリフレッシュ禁止
// mRefreshLayout.setEnabled( false );
}
@Override
public void onItemSwipeEnded( ListSwipeItem item, ListSwipeItem.SwipeDirection swipedDirection ){
// 操作完了でリフレッシュ許可
// mRefreshLayout.setEnabled( USE_SWIPE_REFRESH );
// 左にスワイプした(右端に青が見えた) なら要素を削除する
if( swipedDirection == ListSwipeItem.SwipeDirection.LEFT ){
MyItem adapterItem = (MyItem) item.getTag();
listAdapter.removeItem( listAdapter.getPositionForItem( adapterItem ) );
}
}
} );
}
void restoreData( String svColumnList, int ivSelection ){
this.old_selection = ivSelection;
ArrayList< MyItem > tmp_list = new ArrayList<>();
try{
JSONArray array = new JSONArray( svColumnList );
for( int i = 0, ie = array.length() ; i < ie ; ++ i ){
try{
JSONObject src = array.optJSONObject( i );
MyItem item = new MyItem( src, i );
if( src != null ){
tmp_list.add( item );
if( old_selection == item.old_index ){
item.setOldSelection( true );
}
}
}catch( Throwable ex2 ){
ex2.printStackTrace();
}
}
}catch( Throwable ex ){
ex.printStackTrace();
}
listAdapter.setItemList( tmp_list );
}
void makeResult( int new_selection ){
Intent intent = new Intent();
List< MyItem > item_list = listAdapter.getItemList();
// どの要素を選択するか
if( new_selection >= 0 && new_selection < listAdapter.getItemCount() ){
intent.putExtra( EXTRA_SELECTION, new_selection );
}else{
for( int i = 0, ie = item_list.size() ; i < ie ; ++ i ){
if( item_list.get( i ).bOldSelection ){
intent.putExtra( EXTRA_SELECTION, i );
break;
}
}
}
// 並べ替え用データ
ArrayList< Integer > order_list = new ArrayList<>();
for( MyItem item : item_list ){
order_list.add( item.old_index );
}
intent.putExtra( EXTRA_ORDER, order_list );
setResult( RESULT_OK, intent );
}
private void performItemSelected( MyItem item ){
int idx = listAdapter.getPositionForItem( item );
makeResult( idx );
finish();
}
// リスト要素のデータ
static class MyItem {
long id;
JSONObject json;
String name;
String access;
boolean bOldSelection;
int old_index;
MyItem( JSONObject src, long id ){
this.json = src;
this.name = src.optString( Column.KEY_COLUMN_NAME );
this.access = src.optString( Column.KEY_COLUMN_ACCESS );
this.old_index = src.optInt( Column.KEY_OLD_INDEX );
this.id = id;
}
void setOldSelection( boolean b ){
bOldSelection = b;
}
}
// リスト要素のViewHolder
static class MyViewHolder extends DragItemAdapter.ViewHolder {
final View ivBookmark;
final TextView tvAccess;
final TextView tvName;
MyViewHolder( final View viewRoot ){
super( viewRoot
, R.id.ivDragHandle // View ID ここを押すとドラッグ操作をすぐに開始する
, true // 長押しでドラッグ開始するなら真
);
// リスト要素のビューが ListSwipeItem だった場合Swipe操作を制御できる
if( viewRoot instanceof ListSwipeItem ){
ListSwipeItem lsi = (ListSwipeItem) viewRoot;
lsi.setSwipeInStyle( ListSwipeItem.SwipeInStyle.SLIDE );
lsi.setSupportedSwipeDirection( ListSwipeItem.SwipeDirection.LEFT );
}
ivBookmark = viewRoot.findViewById( R.id.ivBookmark );
tvAccess = (TextView) viewRoot.findViewById( R.id.tvAccess );
tvName = (TextView) viewRoot.findViewById( R.id.tvName );
}
void bind( MyItem item ){
itemView.setTag( item ); // itemView は親クラスのメンバ変数
ivBookmark.setVisibility( item.bOldSelection ? View.VISIBLE: View.INVISIBLE );
tvAccess.setText( item.access );
tvName.setText( item.name );
}
// @Override
// public boolean onItemLongClicked( View view ){
// return false;
// }
@Override
public void onItemClicked( View view ){
MyItem item = (MyItem) itemView.getTag(); // itemView は親クラスのメンバ変数
ActColumnList activity = ( (ActColumnList) Utils.getActivityFromView( view ) );
if( activity != null ) activity.performItemSelected( item );
}
}
// ドラッグ操作中のデータ
private static class MyDragItem extends DragItem {
MyDragItem( Context context, int layoutId ){
super( context, layoutId );
}
@Override
public void onBindDragView( View clickedView, View dragView ){
dragView.findViewById( R.id.ivBookmark ).setVisibility(
clickedView.findViewById( R.id.ivBookmark ).getVisibility()
);
((TextView)dragView.findViewById( R.id.tvAccess )).setText(
((TextView)clickedView.findViewById( R.id.tvAccess )).getText()
);
((TextView)dragView.findViewById( R.id.tvName )).setText(
((TextView)clickedView.findViewById( R.id.tvName )).getText()
);
}
}
private class MyListAdapter extends DragItemAdapter< MyItem, MyViewHolder > {
MyListAdapter(){
super();
setHasStableIds( true );
setItemList( new ArrayList< MyItem >() );
}
@Override
public MyViewHolder onCreateViewHolder( ViewGroup parent, int viewType ){
View view = getLayoutInflater().inflate( R.layout.lv_column_list, parent, false );
return new MyViewHolder( view );
}
@Override
public void onBindViewHolder( MyViewHolder holder, int position ){
super.onBindViewHolder( holder, position );
holder.bind( getItemList().get( position ) );
}
@Override
public long getItemId( int position ){
MyItem item = mItemList.get( position ); // mItemList は親クラスのメンバ変数
return item.id;
}
}
}

View File

@ -4,6 +4,8 @@ import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@ -31,8 +33,7 @@ import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringBufferInputStream;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.HashSet;
import jp.juggler.subwaytooter.api.TootApiClient;
@ -59,11 +60,15 @@ public class ActMain extends AppCompatActivity
// super.attachBaseContext( CalligraphyContextWrapper.wrap(newBase));
// }
SharedPreferences pref;
@Override
protected void onCreate( Bundle savedInstanceState ){
super.onCreate( savedInstanceState );
requestWindowFeature( Window.FEATURE_NO_TITLE );
pref = Pref.pref(this);
initUI();
loadColumnList();
}
@ -106,11 +111,36 @@ public class ActMain extends AppCompatActivity
super.onPause();
}
static final int REQUEST_CODE_COLUMN_LIST =1;
@Override
protected void onActivityResult( int requestCode, int resultCode, Intent data ){
if( resultCode == RESULT_OK && requestCode == REQUEST_CODE_COLUMN_LIST ){
if( data != null ){
ArrayList<Integer> order = data.getIntegerArrayListExtra( ActColumnList.EXTRA_ORDER );
if( order != null ){
pager_adapter.setOrder( pager, order );
}
if( pager_adapter.column_list.isEmpty() ){
llEmpty.setVisibility( View.VISIBLE );
}
int select = data.getIntExtra( ActColumnList.EXTRA_SELECTION ,-1);
if( select != -1 ){
pager.setCurrentItem( select,true );
}
}
}
super.onActivityResult( requestCode, resultCode, data );
}
@Override
public void onBackPressed(){
DrawerLayout drawer = (DrawerLayout) findViewById( R.id.drawer_layout );
if( drawer.isDrawerOpen( GravityCompat.START ) ){
drawer.closeDrawer( GravityCompat.START );
}else if( pref.getBoolean( Pref.KEY_BACK_TO_COLUMN_LIST ,false) ){
performColumnList();
}else if( ! pager_adapter.column_list.isEmpty() ){
performColumnClose( false,pager_adapter.getColumn( pager.getCurrentItem() ) );
}else{
@ -237,7 +267,7 @@ public class ActMain extends AppCompatActivity
}
public void performAccountAdd(){
LoginForm.showLoginForm( this, new LoginForm.LoginFormCallback() {
LoginForm.showLoginForm( this, null,new LoginForm.LoginFormCallback() {
@Override
public void startLogin( final Dialog dialog, final String instance, final String user_mail, final String password ){
@ -311,18 +341,9 @@ public class ActMain extends AppCompatActivity
}
private void onAccountUpdated( SavedAccount data ){
Utils.showToast( this, false, R.string.account_confirmed );
//
llEmpty.setVisibility( View.GONE );
//
Column col = new Column( this, data, Column.TYPE_TL_HOME );
int idx = pager_adapter.addColumn( pager, col );
pager.setCurrentItem( idx );
}
public void performColumnClose( boolean bConfirm,final Column column ){
if(! bConfirm ){
if(! bConfirm && ! pref.getBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN,false ) ){
new AlertDialog.Builder( this )
.setTitle( R.string.confirm )
.setMessage( R.string.close_column )
@ -346,27 +367,55 @@ public class ActMain extends AppCompatActivity
}
}
//////////////////////////////////////////////////////////////
// カラム追加系
void performOpenUser(SavedAccount access_info,TootAccount user){
public void addColumn(SavedAccount ai,int type,long who,long status_id ){
// 既に同じカラムがあればそこに移動する
for( Column column : pager_adapter.column_list ){
if( ai.user.equals( column.access_info.user )
&& column.type == type
&& column.who_id == who
&& column.status_id == status_id
){
pager.setCurrentItem( pager_adapter.column_list.indexOf( column ) ,true);
return;
}
}
llEmpty.setVisibility( View.GONE );
//
Column col = new Column( ActMain.this, access_info, Column.TYPE_TL_STATUSES, user.id );
pager.setCurrentItem( pager_adapter.addColumn( pager, col ) ,true);
Column col = new Column( ActMain.this, ai, type, who,status_id );
int idx = pager_adapter.addColumn( pager, col );
pager.setCurrentItem( idx ,true);
}
private void performAddTimeline( final int type, final Object... params ){
private void onAccountUpdated( SavedAccount data ){
Utils.showToast( this, false, R.string.account_confirmed );
addColumn(data, Column.TYPE_TL_HOME,data.id ,0L );
}
void performOpenUser(SavedAccount access_info,TootAccount user){
addColumn( access_info,Column.TYPE_TL_STATUSES, user.id ,0L);
}
public void performConversation( SavedAccount access_info, TootStatus status ){
addColumn( access_info,Column.TYPE_TL_CONVERSATION, access_info.id, status.id );
}
private void performAddTimeline( final int type ){
AccountPicker.pick( this, new AccountPicker.AccountPickerCallback() {
@Override
public void onAccountPicked( SavedAccount ai ){
llEmpty.setVisibility( View.GONE );
//
Column col = new Column( ActMain.this, ai, type, ai.id, params );
int idx = pager_adapter.addColumn( pager, col );
pager.setCurrentItem( idx ,true);
addColumn( ai, type,ai.id ,0L);
}
} );
}
//////////////////////////////////////////////////////////////
public void openBrowser( String url ){
openChromeTab( url );
}
@ -393,75 +442,16 @@ public class ActMain extends AppCompatActivity
openChromeTab( url );
}
};
static final String FILE_COLUMN_LIST = "column_list";
private void loadColumnList(){
try{
InputStream is = openFileInput( FILE_COLUMN_LIST );
try{
ByteArrayOutputStream bao = new ByteArrayOutputStream( is.available() );
byte[] tmp = new byte[ 4096 ];
for( ; ; ){
int r = is.read( tmp, 0, tmp.length );
if( r <= 0 ) break;
bao.write( tmp, 0, r );
}
JSONArray array = new JSONArray( Utils.decodeUTF8( bao.toByteArray() ) );
for( int i = 0, ie = array.length() ; i < ie ; ++ i ){
try{
JSONObject src = array.optJSONObject( i );
Column col = new Column( ActMain.this, src );
pager_adapter.addColumn( pager, col );
}catch( Throwable ex ){
ex.printStackTrace();
}
}
}finally{
is.close();
}
}catch( FileNotFoundException ignored ){
}catch( Throwable ex ){
ex.printStackTrace();
Utils.showToast( this, ex, "loadColumnList failed." );
}
if( pager_adapter.column_list.size() > 0 ){
llEmpty.setVisibility( View.GONE );
}
}
private void saveColumnList(){
JSONArray array = new JSONArray();
for( Column column : pager_adapter.column_list ){
try{
JSONObject item = new JSONObject();
column.encodeJSON( item );
array.put( item );
}catch( JSONException ex ){
ex.printStackTrace();
}
}
try{
OutputStream os = openFileOutput( FILE_COLUMN_LIST, MODE_PRIVATE );
try{
os.write( Utils.encodeUTF8( array.toString() ) );
}finally{
os.close();
}
}catch( Throwable ex ){
ex.printStackTrace();
Utils.showToast( this, ex, "saveColumnList failed." );
}
}
private void performTootButton(){
private void performTootButton(){
Column c = pager_adapter.getColumn( pager.getCurrentItem() );
if( c != null && c.access_info != null ){
ActPost.open( this, c.access_info.db_id ,null );
}
}
public void performReply( SavedAccount account, TootStatus status ){
ActPost.open( this, account.db_id ,status );
}
/////////////////////////////////////////////////////////////////////////
@ -710,17 +700,10 @@ public class ActMain extends AppCompatActivity
Utils.showToast( this,false,"not implemented. toot="+status.decoded_content );
}
public void performReply( SavedAccount account, TootStatus status ){
Utils.showToast( this,false,"not implemented. toot="+status.decoded_content );
}
////////////////////////////////////////
private void performColumnList(){
Utils.showToast( this,false,"not implemented." );
}
private void performAccountSetting(){
AccountPicker.pick( this, new AccountPicker.AccountPickerCallback() {
@ -732,8 +715,89 @@ public class ActMain extends AppCompatActivity
}
private void performAppSetting(){
Utils.showToast( this,false,"not implemented." );
ActAppSetting.open( ActMain.this);
}
////////////////////////////////////////////////////////
// column list
JSONArray encodeColumnList(){
JSONArray array = new JSONArray();
for(int i=0,ie = pager_adapter.column_list.size(); i<ie;++i){
Column column = pager_adapter.column_list.get(i);
try{
JSONObject dst = new JSONObject();
column.encodeJSON( dst ,i);
array.put( dst );
}catch( JSONException ex ){
ex.printStackTrace();
}
}
return array;
}
private void performColumnList(){
Intent intent = new Intent(this,ActColumnList.class);
intent.putExtra(ActColumnList.EXTRA_ORDER,encodeColumnList().toString() );
intent.putExtra(ActColumnList.EXTRA_SELECTION,pager.getCurrentItem() );
startActivityForResult( intent ,REQUEST_CODE_COLUMN_LIST );
}
static final String FILE_COLUMN_LIST = "column_list";
private void saveColumnList(){
JSONArray array = encodeColumnList();
try{
OutputStream os = openFileOutput( FILE_COLUMN_LIST, MODE_PRIVATE );
try{
os.write( Utils.encodeUTF8( array.toString() ) );
}finally{
os.close();
}
}catch( Throwable ex ){
ex.printStackTrace();
Utils.showToast( this, ex, "saveColumnList failed." );
}
}
private void loadColumnList(){
try{
InputStream is = openFileInput( FILE_COLUMN_LIST );
try{
ByteArrayOutputStream bao = new ByteArrayOutputStream( is.available() );
byte[] tmp = new byte[ 4096 ];
for( ; ; ){
int r = is.read( tmp, 0, tmp.length );
if( r <= 0 ) break;
bao.write( tmp, 0, r );
}
JSONArray array = new JSONArray( Utils.decodeUTF8( bao.toByteArray() ) );
for( int i = 0, ie = array.length() ; i < ie ; ++ i ){
try{
JSONObject src = array.optJSONObject( i );
Column col = new Column( ActMain.this, src );
pager_adapter.addColumn( pager, col );
}catch( Throwable ex ){
ex.printStackTrace();
}
}
}finally{
is.close();
}
}catch( FileNotFoundException ignored ){
}catch( Throwable ex ){
ex.printStackTrace();
Utils.showToast( this, ex, "loadColumnList failed." );
}
if( pager_adapter.column_list.size() > 0 ){
llEmpty.setVisibility( View.GONE );
}
}
}

View File

@ -29,6 +29,7 @@ import com.android.volley.toolbox.NetworkImageView;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
@ -39,8 +40,10 @@ import java.util.Comparator;
import jp.juggler.subwaytooter.api.TootApiClient;
import jp.juggler.subwaytooter.api.TootApiResult;
import jp.juggler.subwaytooter.api.entity.TootAttachment;
import jp.juggler.subwaytooter.api.entity.TootMention;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
import okhttp3.MediaType;
@ -53,13 +56,21 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
static final LogCategory log = new LogCategory( "ActPost" );
static final String KEY_ACCOUNT_DB_ID = "account_db_id";
static final String KEY_VISIBILITY = "visibility";
static final String KEY_ATTACHMENT_LIST = "attachment_list";
static final String KEY_REPLY_STATUS = "reply_status";
public static void open( Context context, long account_db_id, String visibility ){
static final String KEY_ATTACHMENT_LIST = "attachment_list";
static final String KEY_VISIBILITY = "visibility";
static final String KEY_IN_REPLY_TO_ID = "in_reply_to_id";
static final String KEY_IN_REPLY_TO_TEXT = "in_reply_to_text";
static final String KEY_IN_REPLY_TO_IMAGE = "in_reply_to_image";
public static void open( Context context, long account_db_id, TootStatus reply_status ){
Intent intent = new Intent( context, ActPost.class );
intent.putExtra( KEY_ACCOUNT_DB_ID, account_db_id );
if( visibility != null ) intent.putExtra( KEY_VISIBILITY, visibility );
if( reply_status != null ){
intent.putExtra( KEY_REPLY_STATUS, reply_status.json.toString() );
}
context.startActivity( intent );
}
@ -94,10 +105,13 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
case R.id.btnPost:
performPost();
break;
case R.id.btnRemoveReply:
removeReply();
break;
}
}
static final int REQUEST_CODE_ATTACHMENT = 1;
@Override
@ -143,11 +157,9 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
}
}
String sv = savedInstanceState.getString( KEY_VISIBILITY );
if( TextUtils.isEmpty( sv ) ) sv = account.visibility;
this.visibility = sv;
this.visibility = savedInstanceState.getString( KEY_VISIBILITY );
sv = savedInstanceState.getString( KEY_ATTACHMENT_LIST );
String sv = savedInstanceState.getString( KEY_ATTACHMENT_LIST );
if( ! TextUtils.isEmpty( sv ) ){
try{
attachment_list.clear();
@ -168,6 +180,10 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
ex.printStackTrace();
}
}
this.in_reply_to_id = savedInstanceState.getLong( KEY_IN_REPLY_TO_ID, - 1L );
this.in_reply_to_text = savedInstanceState.getString( KEY_IN_REPLY_TO_TEXT );
this.in_reply_to_image = savedInstanceState.getString( KEY_IN_REPLY_TO_IMAGE );
}else{
Intent intent = getIntent();
long account_db_id = intent.getLongExtra( KEY_ACCOUNT_DB_ID, SavedAccount.INVALID_ID );
@ -181,9 +197,75 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
}
}
String sv = intent.getStringExtra( KEY_VISIBILITY );
if( TextUtils.isEmpty( sv ) ) sv = account.visibility;
this.visibility = sv;
String sv = intent.getStringExtra( KEY_REPLY_STATUS );
if( sv != null ){
try{
TootStatus repley_status = TootStatus.parse( log, new JSONObject( sv ) );
// CW をリプライ元に合わせる
if( ! TextUtils.isEmpty( repley_status.spoiler_text ) ){
cbContentWarning.setChecked( true );
etContentWarning.setText( repley_status.spoiler_text );
}
// mention を自動設定する
String acct_me = account.getFullAcct( account );
ArrayList< String > mention_list = new ArrayList<>();
mention_list.add( account.getFullAcct( repley_status.account ) );
if( repley_status.mentions != null ){
for( TootMention mention : repley_status.mentions ){
sv = account.getFullAcct( mention.acct );
if( !sv.equals( acct_me ) && ! mention_list.contains( sv ) ){
mention_list.add( sv );
}
}
}
StringBuilder sb = new StringBuilder();
for( String acct : mention_list ){
if( sb.length() > 0 ) sb.append( ' ' );
sb.append( acct );
}
if( sb.length() > 0 ){
sb.append( ' ' );
etContent.setText( sb.toString() );
etContent.setSelection( sb.length() );
}
// リプライ表示をつける
in_reply_to_id = repley_status.id;
in_reply_to_text = repley_status.content;
in_reply_to_image = repley_status.account.avatar_static;
// 公開範囲
try{
// 比較する前にデフォルトの公開範囲を計算する
if( TextUtils.isEmpty( visibility ) ){
visibility = account.visibility;
if( TextUtils.isEmpty( visibility ) ){
visibility = TootStatus.VISIBILITY_PUBLIC;
}
}
// デフォルトの方が公開範囲が大きい場合リプライ元に合わせて公開範囲を狭める
int i = TootStatus.compareVisibility( this.visibility, repley_status.visibility );
if( i > 0 ){ // より大きい=>より公開範囲が広い
this.visibility = repley_status.visibility;
}
}catch( Throwable ex ){
ex.printStackTrace();
}
}catch( Throwable ex ){
ex.printStackTrace();
}
}
}
if( TextUtils.isEmpty( visibility ) ){
visibility = account.visibility;
if( TextUtils.isEmpty( visibility ) ){
visibility = TootStatus.VISIBILITY_PUBLIC;
}
}
if( this.account == null ){
@ -194,6 +276,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
showMediaAttachment();
updateVisibility();
updateTextCount();
showReplyTo();
}
@Override
@ -214,6 +297,11 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
}
outState.putString( KEY_ATTACHMENT_LIST, array.toString() );
}
outState.putLong( KEY_IN_REPLY_TO_ID, in_reply_to_id );
outState.putString( KEY_IN_REPLY_TO_TEXT, in_reply_to_text );
outState.putString( KEY_IN_REPLY_TO_IMAGE, in_reply_to_image );
}
Button btnAccount;
@ -230,8 +318,14 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
EditText etContentWarning;
EditText etContent;
TextView tvCharCount;
ArrayList< SavedAccount > account_list;
View llReply;
TextView tvReplyTo;
View btnRemoveReply;
NetworkImageView ivReply;
private void initUI(){
setContentView( R.layout.act_post );
@ -250,6 +344,11 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
etContent = (EditText) findViewById( R.id.etContent );
tvCharCount = (TextView) findViewById( R.id.tvCharCount );
llReply = findViewById( R.id.llReply );
tvReplyTo = (TextView) findViewById( R.id.tvReplyTo );
btnRemoveReply = findViewById( R.id.btnRemoveReply );
ivReply= (NetworkImageView) findViewById( R.id.ivReply );
account_list = SavedAccount.loadAccountList( log );
Collections.sort( account_list, new Comparator< SavedAccount >() {
@Override
@ -262,6 +361,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
btnVisibility.setOnClickListener( this );
btnAttachment.setOnClickListener( this );
btnPost.setOnClickListener( this );
btnRemoveReply.setOnClickListener( this );
ivMedia1.setOnClickListener( this );
ivMedia2.setOnClickListener( this );
ivMedia3.setOnClickListener( this );
@ -312,12 +412,22 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
}
private void performAccountChooser(){
// TODO: mention の状況によっては別サーバを選べないかもしれない
// TODO: 添付ファイルがあったら確認の上添付ファイルを捨てないと切り替えられない
if( ! attachment_list.isEmpty() ){
// 添付ファイルがあったら確認の上添付ファイルを捨てないと切り替えられない
Utils.showToast( this,false,R.string.cant_change_account_when_attachiment_specified );
}
final ArrayList< SavedAccount > tmp_account_list = new ArrayList<>();
tmp_account_list.addAll( account_list );
if( in_reply_to_id != -1L ){
// リプライは数値IDなのでサーバが同じじゃないと選択できない
for( SavedAccount a : account_list ){
if( !a.host.equals( account.host ) ) continue;
tmp_account_list.add(a);
}
}else{
tmp_account_list.addAll( account_list );
}
String[] caption_list = new String[ tmp_account_list.size() ];
for( int i = 0, ie = tmp_account_list.size() ; i < ie ; ++ i ){
caption_list[ i ] = tmp_account_list.get( i ).user;
@ -371,11 +481,11 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
iv.setVisibility( View.VISIBLE );
PostAttachment a = attachment_list.get( idx );
if( a.status == ATTACHMENT_UPLOADING ){
iv.setImageDrawable( ContextCompat.getDrawable(this,R.drawable.ic_loading ));
iv.setImageDrawable( ContextCompat.getDrawable( this, R.drawable.ic_loading ) );
}else if( a.attachment != null ){
iv.setImageUrl( a.attachment.preview_url, App1.getImageLoader() );
}else{
iv.setImageDrawable( ContextCompat.getDrawable(this,R.drawable.ic_unknown ));
iv.setImageDrawable( ContextCompat.getDrawable( this, R.drawable.ic_unknown ) );
}
}
}
@ -535,8 +645,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
attachment_list.remove( pa );
}else{
String sv = etContent.getText().toString();
sv = sv + pa.attachment.text_url+" ";
etContent.setText(sv);
sv = sv + pa.attachment.text_url + " ";
etContent.setText( sv );
}
showMediaAttachment();
@ -575,10 +685,10 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
//////////////////////////////////////////////////////////////////////
// visibility
String visibility = TootStatus.VISIBILITY_PUBLIC;
String visibility;
private void updateVisibility(){
btnVisibility.setImageResource( Styler.getVisibilityIcon(visibility) );
btnVisibility.setImageResource( Styler.getVisibilityIcon( visibility ) );
}
private void performVisibility(){
@ -626,12 +736,12 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
private void performPost(){
final String content = etContent.getText().toString().trim();
if(TextUtils.isEmpty( content ) ){
Utils.showToast( this,true,R.string.post_error_contents_empty );
if( TextUtils.isEmpty( content ) ){
Utils.showToast( this, true, R.string.post_error_contents_empty );
return;
}
final String spoiler_text;
if( !cbContentWarning.isChecked() ){
if( ! cbContentWarning.isChecked() ){
spoiler_text = null;
}else{
spoiler_text = etContentWarning.getText().toString().trim();
@ -641,29 +751,33 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
}
}
final StringBuilder sb = new StringBuilder();
final StringBuilder sb = new StringBuilder( );
sb.append("status=");
sb.append(Uri.encode( content ));
sb.append("&visibility=");
sb.append(Uri.encode( visibility ));
sb.append( "status=" );
sb.append( Uri.encode( content ) );
sb.append( "&visibility=" );
sb.append( Uri.encode( visibility ) );
if( cbNSFW.isChecked() ){
sb.append("&sensitive=1");
sb.append( "&sensitive=1" );
}
if( spoiler_text != null ){
sb.append("&spoiler_text=");
sb.append(Uri.encode( spoiler_text ));
sb.append( "&spoiler_text=" );
sb.append( Uri.encode( spoiler_text ) );
}
for(PostAttachment pa : attachment_list){
if( in_reply_to_id != - 1L ){
sb.append( "&in_reply_to_id=" );
sb.append( Long.toString( in_reply_to_id ) );
}
for( PostAttachment pa : attachment_list ){
if( pa.attachment != null ){
sb.append("&media_ids[]="+pa.attachment.id);
sb.append( "&media_ids[]=" + pa.attachment.id );
}
}
// TODO: in_reply_to_id (optional): local ID of the status you want to reply to
final ProgressDialog progress = new ProgressDialog( this );
@ -671,7 +785,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
final SavedAccount target_account = account;
TootStatus status;
@Override
protected TootApiResult doInBackground( Void... params ){
TootApiClient client = new TootApiClient( ActPost.this, new TootApiClient.Callback() {
@ -690,21 +804,21 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
} );
}
} );
client.setAccount( target_account );
Request.Builder request_builder = new Request.Builder()
.post( RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
,sb.toString()
));
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, sb.toString()
) );
TootApiResult result = client.request( "/api/v1/statuses", request_builder );
if( result.object != null ){
status = TootStatus.parse( log,result.object );
status = TootStatus.parse( log, result.object );
}
return result;
}
@Override
@ -739,5 +853,26 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
AsyncTaskCompat.executeParallel( task );
}
/////////////////////////////////////////////////
long in_reply_to_id = - 1L;
String in_reply_to_text;
String in_reply_to_image;
void showReplyTo(){
if( in_reply_to_id == - 1L ){
llReply.setVisibility( View.GONE );
}else{
llReply.setVisibility( View.VISIBLE );
tvReplyTo.setText( HTMLDecoder.decodeHTML( in_reply_to_text ) );
ivReply.setImageUrl( in_reply_to_image,App1.getImageLoader() );
}
}
private void removeReply(){
in_reply_to_id = - 1L;
in_reply_to_text = null;
in_reply_to_image = null;
showReplyTo();
}
}

View File

@ -31,7 +31,6 @@ public class App1 extends Application {
super.onCreate();
CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
.setDefaultFontPath("NotoSansCJKjp-Regular.otf")
.setFontAttrId(R.attr.fontPath)
.build()
);
@ -40,14 +39,11 @@ public class App1 extends Application {
typeface_emoji = TypefaceUtils.load(getAssets(), "emojione_android.ttf");
}
if( typeface_normal == null ){
typeface_normal = TypefaceUtils.load(getAssets(), "NotoSansCJKjp-Regular.otf");
}
if( db_open_helper == null ){
db_open_helper = new DBOpenHelper( getApplicationContext() );
db_open_helper.onCreate( getDB() );
}
if( image_loader == null ){
image_loader = new MyImageLoader(
Volley.newRequestQueue( getApplicationContext() )
@ -156,5 +152,4 @@ public class App1 extends Application {
public static final OkHttpClient ok_http_client = new OkHttpClient();
public static Typeface typeface_emoji ;
public static Typeface typeface_normal ;
}

View File

@ -2,23 +2,32 @@ package jp.juggler.subwaytooter;
import android.os.AsyncTask;
import android.support.v4.os.AsyncTaskCompat;
import android.text.TextUtils;
import android.util.SparseLongArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jp.juggler.subwaytooter.api.TootApiClient;
import jp.juggler.subwaytooter.api.TootApiResult;
import jp.juggler.subwaytooter.api.entity.TootAccount;
import jp.juggler.subwaytooter.api.entity.TootContext;
import jp.juggler.subwaytooter.api.entity.TootId;
import jp.juggler.subwaytooter.api.entity.TootNotification;
import jp.juggler.subwaytooter.api.entity.TootReport;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
import okhttp3.Headers;
public class Column {
static final LogCategory log = new LogCategory( "Column" );
@ -26,11 +35,17 @@ public class Column {
static final String KEY_ACCOUNT_ROW_ID = "account_id";
static final String KEY_TYPE = "type";
static final String KEY_WHO_ID = "who_id";
static final String KEY_STATUS_ID = "status_id";
static final String KEY_COLUMN_ACCESS = "column_access";
static final String KEY_COLUMN_NAME = "column_name";
static final String KEY_OLD_INDEX = "old_index";
final ActMain activity;
final SavedAccount access_info;
final int type;
final long who_id;
long status_id;
static final int TYPE_TL_HOME = 1;
static final int TYPE_TL_LOCAL = 2;
@ -39,6 +54,7 @@ public class Column {
static final int TYPE_TL_FAVOURITES = 5;
static final int TYPE_TL_REPORTS = 6;
static final int TYPE_TL_NOTIFICATIONS = 7;
static final int TYPE_TL_CONVERSATION = 8;
public Column( ActMain activity, SavedAccount access_info, int type ){
this( activity, access_info, type, access_info.id );
@ -49,6 +65,11 @@ public class Column {
this.access_info = access_info;
this.type = type;
this.who_id = who_id;
if( type == TYPE_TL_CONVERSATION ){
if( params==null || params.length < 1 ) throw new IndexOutOfBoundsException( "TYPE_TL_CONVERSATION requires status_id as Long" );
if( !( params[0] instanceof Long ) )throw new IllegalArgumentException( "TYPE_TL_CONVERSATION status_id is not Long" );
status_id = (Long) params[0];
}
startLoading();
}
@ -58,6 +79,7 @@ public class Column {
if( access_info == null ) throw new RuntimeException( "missing account" );
this.type = src.optInt( KEY_TYPE );
this.who_id = src.optLong( KEY_WHO_ID );
this.status_id = src.optLong( KEY_STATUS_ID );
startLoading();
}
@ -67,10 +89,16 @@ public class Column {
is_dispose.set( true );
}
public void encodeJSON( JSONObject item ) throws JSONException{
item.put( KEY_ACCOUNT_ROW_ID , access_info.db_id );
public void encodeJSON( JSONObject item, int old_index ) throws JSONException{
item.put( KEY_ACCOUNT_ROW_ID, access_info.db_id );
item.put( KEY_TYPE, type );
item.put( KEY_WHO_ID, who_id );
item.put( KEY_STATUS_ID,status_id);
// 以下は保存には必要ないがカラムリスト画面で使う
item.put( KEY_COLUMN_ACCESS, access_info.user );
item.put( KEY_COLUMN_NAME, getColumnName() );
item.put( KEY_OLD_INDEX, old_index );
}
public String getColumnName(){
@ -97,15 +125,17 @@ public class Column {
case TYPE_TL_NOTIFICATIONS:
return activity.getString( R.string.notifications );
case TYPE_TL_CONVERSATION:
return activity.getString( R.string.conversation_around,status_id );
}
}
public interface StatusEntryCallback{
void onIterate(TootStatus status);
public interface StatusEntryCallback {
void onIterate( TootStatus status );
}
public void findStatus( SavedAccount target_account,long target_status_id ,StatusEntryCallback callback){
// ブーストやお気に入りの更新に使うステータスを列挙する
public void findStatus( SavedAccount target_account, long target_status_id, StatusEntryCallback callback ){
if( target_account.user.equals( access_info.user ) ){
for( int i = 0, ie = status_list.size() ; i < ie ; ++ i ){
TootStatus status = status_list.get( i );
@ -113,7 +143,7 @@ public class Column {
callback.onIterate( status );
}
TootStatus reblog = status.reblog;
if( reblog!= null ){
if( reblog != null ){
if( target_status_id == reblog.id ){
callback.onIterate( status );
}
@ -157,12 +187,24 @@ public class Column {
AsyncTask< Void, Void, TootApiResult > last_task;
void cancelLastTask(){
if( last_task != null ) last_task.cancel( true );
if( last_task != null ){
last_task.cancel( true );
last_task = null;
//
bInitialLoading = false;
bRefreshLoading = false;
mInitialLoadingError = activity.getString( R.string.cancelled );
//
}
}
boolean is_loading = false;
boolean bInitialLoading;
boolean bRefreshLoading;
String mInitialLoadingError;
String mRefreshLoadingError;
String task_progress;
String error = null;
final TootStatus.List status_list = new TootStatus.List();
final TootReport.List report_list = new TootReport.List();
@ -174,12 +216,23 @@ public class Column {
startLoading();
}
static final String PATH_TL_HOME = "/api/v1/timelines/home?limit=80";
static final String PATH_TL_LOCAL = "/api/v1/timelines/public?limit=80&local=1";
static final String PATH_TL_FEDERATE = "/api/v1/timelines/public?limit=80";
static final String PATH_TL_FAVOURITES = "/api/v1/favourites?limit=80";
static final String PATH_TL_REPORTS = "/api/v1/reports?limit=80";
static final String PATH_TL_NOTIFICATIONS = "/api/v1/notifications?limit=80";
void startLoading(){
error = null;
is_loading = true;
fireVisualCallback();
cancelLastTask();
mInitialLoadingError = null;
bInitialLoading = true;
max_id = null;
since_id = null;
fireVisualCallback();
AsyncTask< Void, Void, TootApiResult > task = this.last_task = new AsyncTask< Void, Void, TootApiResult >() {
TootStatus.List tmp_list_status;
@ -188,6 +241,7 @@ public class Column {
TootApiResult parseStatuses( TootApiResult result ){
if( result != null ){
saveRange( result, true, true );
tmp_list_status = TootStatus.parseList( log, result.array );
}
return result;
@ -195,6 +249,7 @@ public class Column {
TootApiResult parseAccount( TootApiResult result ){
if( result != null ){
saveRange( result, true, true );
who_account = TootAccount.parse( log, result.object );
}
return result;
@ -202,6 +257,7 @@ public class Column {
TootApiResult parseReports( TootApiResult result ){
if( result != null ){
saveRange( result, true, true );
tmp_list_report = TootReport.parseList( log, result.array );
}
return result;
@ -209,6 +265,7 @@ public class Column {
TootApiResult parseNotifications( TootApiResult result ){
if( result != null ){
saveRange( result, true, true );
tmp_list_notification = TootNotification.parseList( log, result.array );
}
return result;
@ -240,30 +297,45 @@ public class Column {
switch( type ){
default:
case TYPE_TL_HOME:
return parseStatuses( client.request( "/api/v1/timelines/home" ) );
return parseStatuses( client.request( PATH_TL_HOME ) );
case TYPE_TL_LOCAL:
return parseStatuses( client.request( "/api/v1/timelines/public?local=1" ) );
return parseStatuses( client.request( PATH_TL_LOCAL ) );
case TYPE_TL_FEDERATE:
return parseStatuses( client.request( "/api/v1/timelines/public" ) );
return parseStatuses( client.request( PATH_TL_FEDERATE ) );
case TYPE_TL_STATUSES:
if( who_account == null ){
parseAccount( client.request( "/api/v1/accounts/" + who_id ) );
parseAccount( client.request( "/api/v1/accounts/" + who_id + "?limit=80" ) );
client.callback.publishApiProgress( "" );
}
return parseStatuses( client.request( "/api/v1/accounts/" + who_id + "/statuses" ) );
return parseStatuses( client.request( "/api/v1/accounts/" + who_id + "/statuses?limit=80" ) );
case TYPE_TL_FAVOURITES:
return parseStatuses( client.request( "/api/v1/favourites" ) );
return parseStatuses( client.request( PATH_TL_FAVOURITES ) );
case TYPE_TL_REPORTS:
return parseReports( client.request( "/api/v1/reports" ) );
return parseReports( client.request( PATH_TL_REPORTS ) );
case TYPE_TL_NOTIFICATIONS:
return parseNotifications( client.request( "/api/v1/notifications" ) );
return parseNotifications( client.request( PATH_TL_NOTIFICATIONS ) );
case TYPE_TL_CONVERSATION:
TootApiResult result = client.request( "/api/v1/statuses/"+status_id );
if( result== null || result.object == null ) return result;
TootStatus target_status = TootStatus.parse( log,result.object );
target_status.conversation_main = true;
//
result = client.request( "/api/v1/statuses/"+status_id+"/context" );
if( result== null || result.object == null ) return result;
TootContext context = TootContext.parse( log,result.object );
tmp_list_status = new TootStatus.List();
if( context.ancestors != null ) tmp_list_status.addAll( context.ancestors);
tmp_list_status.add(target_status);
if( context.descendants != null ) tmp_list_status.addAll( context.descendants);
return result;
}
}
@ -274,11 +346,16 @@ public class Column {
@Override
protected void onPostExecute( TootApiResult result ){
is_loading = false;
if( result == null ){
Column.this.error = activity.getString( R.string.cancelled );
}else if( result.error != null ){
Column.this.error = result.error;
if( isCancelled() || result == null ){
return;
}
bInitialLoading = false;
last_task = null;
if( result.error != null ){
Column.this.mInitialLoadingError = result.error;
}else{
switch( type ){
default:
@ -287,27 +364,16 @@ public class Column {
case TYPE_TL_FEDERATE:
case TYPE_TL_STATUSES:
case TYPE_TL_FAVOURITES:
if( tmp_list_status != null ){
for( int i = tmp_list_status.size() - 1 ; i >= 0 ; -- i ){
status_list.add( 0, tmp_list_status.get( i ) );
}
}
case TYPE_TL_CONVERSATION:
initList( status_list, tmp_list_status );
break;
case TYPE_TL_REPORTS:
if( tmp_list_report != null ){
for( int i = tmp_list_report.size() - 1 ; i >= 0 ; -- i ){
report_list.add( 0, tmp_list_report.get( i ) );
}
}
initList( report_list, tmp_list_report );
break;
case TYPE_TL_NOTIFICATIONS:
if( tmp_list_notification != null ){
for( int i = tmp_list_notification.size() - 1 ; i >= 0 ; -- i ){
notification_list.add( 0, tmp_list_notification.get( i ) );
}
}
initList( notification_list, tmp_list_notification );
break;
}
@ -318,4 +384,224 @@ public class Column {
AsyncTaskCompat.executeParallel( task );
}
static final Pattern reMaxId = Pattern.compile( "&max_id=(\\d+)" ); // より古いデータの取得に使う
static final Pattern reSinceId = Pattern.compile( "&since_id=(\\d+)" ); // より新しいデータの取得に使う
String max_id;
String since_id;
private void saveRange( TootApiResult result, boolean bBottom, boolean bTop ){
// Link: <https://mastodon.juggler.jp/api/v1/timelines/home?limit=80&max_id=405228>; rel="next",
// <https://mastodon.juggler.jp/api/v1/timelines/home?limit=80&since_id=436946>; rel="prev"
if( result.response != null ){
String sv = result.response.header( "Link" );
if( ! TextUtils.isEmpty( sv ) ){
if( bBottom ){
Matcher m = reMaxId.matcher( sv );
if( m.find() ){
max_id = m.group( 1 );
log.d( "col=%s,max_id=%s", this.hashCode(), max_id );
}
}
if( bTop ){
Matcher m = reSinceId.matcher( sv );
if( m.find() ){
since_id = m.group( 1 );
log.d( "col=%s,since_id=%s", this.hashCode(), since_id );
}
}
}
}
}
String addRange( boolean bBottom, String path ){
char delm = ( - 1 != path.indexOf( '?' ) ? '&' : '?' );
if( bBottom ){
if( max_id != null ) return path + delm + "max_id=" + max_id;
}else{
if( since_id != null ) return path + delm + "since_id=" + since_id;
}
return path;
}
< T extends TootId > void initList( ArrayList< T > dst, ArrayList< T > src ){
if( src == null ) return;
dst.clear();
dst.addAll( src );
}
< T extends TootId > void mergeList( ArrayList< T > dst, ArrayList< T > src, boolean bBottom ){
// 古いリストにある要素の集合
HashSet< Long > id_set = new HashSet();
for( T t : dst ){
id_set.add( t.id );
}
ArrayList< T > tmp_list = new ArrayList<>( src.size() );
for( T t : src ){
if( id_set.contains( t.id ) ) continue;
tmp_list.add( t );
}
if( ! bBottom ){
tmp_list.addAll( dst );
dst.clear();
dst.addAll( tmp_list );
}else{
dst.addAll( tmp_list );
}
}
public boolean startRefresh( final boolean bBottom ){
if( last_task != null ){
log.d( "busy" );
return false;
}else if( bBottom && max_id == null ){
log.d( "startRefresh failed. missing max_id" );
return false;
}else if( !bBottom && since_id == null ){
log.d( "startRefresh failed. missing since_id" );
return false;
}
bRefreshLoading = true;
mRefreshLoadingError = null;
AsyncTask< Void, Void, TootApiResult > task = this.last_task = new AsyncTask< Void, Void, TootApiResult >() {
TootStatus.List tmp_list_status;
TootReport.List tmp_list_report;
TootNotification.List tmp_list_notification;
TootApiResult parseStatuses( TootApiResult result ){
if( result != null ){
saveRange( result, bBottom, ! bBottom );
tmp_list_status = TootStatus.parseList( log, result.array );
}
return result;
}
TootApiResult parseAccount( TootApiResult result ){
if( result != null ){
saveRange( result, bBottom, ! bBottom );
who_account = TootAccount.parse( log, result.object );
}
return result;
}
TootApiResult parseReports( TootApiResult result ){
if( result != null ){
saveRange( result, bBottom, ! bBottom );
tmp_list_report = TootReport.parseList( log, result.array );
}
return result;
}
TootApiResult parseNotifications( TootApiResult result ){
if( result != null ){
saveRange( result, bBottom, ! bBottom );
tmp_list_notification = TootNotification.parseList( log, result.array );
}
return result;
}
@Override
protected TootApiResult doInBackground( Void... params ){
TootApiClient client = new TootApiClient( activity, new TootApiClient.Callback() {
@Override
public boolean isApiCancelled(){
return isCancelled() || is_dispose.get();
}
@Override
public void publishApiProgress( final String s ){
Utils.runOnMainThread( new Runnable() {
@Override
public void run(){
if( isCancelled() ) return;
task_progress = s;
fireVisualCallback();
}
} );
}
} );
client.setAccount( access_info );
switch( type ){
default:
case TYPE_TL_HOME:
return parseStatuses( client.request( addRange( bBottom, PATH_TL_HOME ) ) );
case TYPE_TL_LOCAL:
return parseStatuses( client.request( addRange( bBottom, PATH_TL_LOCAL ) ) );
case TYPE_TL_FEDERATE:
return parseStatuses( client.request( addRange( bBottom, PATH_TL_FEDERATE ) ) );
case TYPE_TL_STATUSES:
if( who_account == null ){
parseAccount( client.request( "/api/v1/accounts/" + who_id + "?limit=80" ) );
client.callback.publishApiProgress( "" );
}
return parseStatuses( client.request( addRange( bBottom, "/api/v1/accounts/" + who_id + "/statuses?limit=80" ) ) );
case TYPE_TL_FAVOURITES:
return parseStatuses( client.request( addRange( bBottom, PATH_TL_FAVOURITES ) ) );
case TYPE_TL_REPORTS:
return parseReports( client.request( addRange( bBottom, PATH_TL_REPORTS ) ) );
case TYPE_TL_NOTIFICATIONS:
return parseNotifications( client.request( addRange( bBottom, PATH_TL_NOTIFICATIONS ) ) );
}
}
@Override
protected void onCancelled( TootApiResult result ){
onPostExecute( null );
}
@Override
protected void onPostExecute( TootApiResult result ){
if( isCancelled() || result == null ){
return;
}
last_task = null;
bRefreshLoading = false;
if( result.error != null ){
Column.this.mRefreshLoadingError = result.error;
}else{
switch( type ){
default:
case TYPE_TL_HOME:
case TYPE_TL_LOCAL:
case TYPE_TL_FEDERATE:
case TYPE_TL_STATUSES:
case TYPE_TL_FAVOURITES:
mergeList( status_list, tmp_list_status, bBottom );
break;
case TYPE_TL_REPORTS:
mergeList( report_list, tmp_list_report, bBottom );
break;
case TYPE_TL_NOTIFICATIONS:
mergeList( notification_list, tmp_list_notification, bBottom );
break;
}
}
fireVisualCallback();
}
};
AsyncTaskCompat.executeParallel( task );
return true;
}
}

View File

@ -5,11 +5,13 @@ import android.app.Activity;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.HashSet;
public class ColumnPagerAdapter extends PagerAdapter{
@ -47,10 +49,29 @@ public class ColumnPagerAdapter extends PagerAdapter{
pager.setAdapter( null );
column_list.remove( idx_column );
pager.setAdapter( this );
}
public void setOrder( ViewPager pager,ArrayList< Integer > order ){
pager.setAdapter( null );
ArrayList<Column> tmp_list = new ArrayList<>();
HashSet<Integer> used_set = new HashSet<>( );
for(Integer i : order){
used_set.add( i );
tmp_list.add( column_list.get(i));
}
for(int i=0,ie=column_list.size();i<ie;++i){
if( used_set.contains( i ) ) continue;
column_list.get(i).dispose();
}
column_list.clear();
column_list.addAll( tmp_list );
pager.setAdapter( this );
}
public Column getColumn( int idx ){
if( idx >= 0 && idx < column_list.size() ) return column_list.get( idx );
@ -84,7 +105,7 @@ public class ColumnPagerAdapter extends PagerAdapter{
//
holder_list.put( page_idx, holder );
//
holder.onPageCreate( root );
holder.onPageCreate( root ,page_idx,column_list.size());
return root;
}

View File

@ -7,6 +7,7 @@ import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageButton;
@ -15,6 +16,8 @@ import android.widget.ListView;
import android.widget.TextView;
import com.android.volley.toolbox.NetworkImageView;
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout;
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@ -27,11 +30,11 @@ import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.table.ContentWarning;
import jp.juggler.subwaytooter.table.MediaShown;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.Emojione;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback {
public class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, SwipyRefreshLayout.OnRefreshListener {
static final LogCategory log = new LogCategory( "ColumnViewHolder" );
public final AtomicBoolean is_destroyed = new AtomicBoolean( false );
@ -56,10 +59,14 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
TextView tvColumnName;
StatusListAdapter status_adapter;
HeaderViewHolder vh_header;
SwipyRefreshLayout swipyRefreshLayout;
void onPageCreate( View root ){
void onPageCreate( View root, int page_idx, int page_count ){
log.d( "onPageCreate:%s", column.getColumnName() );
( (TextView) root.findViewById( R.id.tvColumnIndex ) )
.setText( activity.getString( R.string.column_index, page_idx + 1, page_count ) );
tvColumnName = (TextView) root.findViewById( R.id.tvColumnName );
tvColumnContext = (TextView) root.findViewById( R.id.tvColumnContext );
@ -70,12 +77,20 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
listView = (ListView) root.findViewById( R.id.listView );
status_adapter = new StatusListAdapter();
listView.setAdapter( status_adapter );
listView.setOnItemClickListener( status_adapter );
this.swipyRefreshLayout = (SwipyRefreshLayout) root.findViewById( R.id.swipyRefreshLayout );
swipyRefreshLayout.setOnRefreshListener( this );
if( column.type == Column.TYPE_TL_STATUSES ){
vh_header = new HeaderViewHolder( activity, listView );
listView.addHeaderView( vh_header.viewRoot );
}
if( column.type == Column.TYPE_TL_CONVERSATION ){
swipyRefreshLayout.setEnabled( false );
}
//
column.addVisualListener( this );
@ -102,7 +117,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
void showError( String message ){
tvLoading.setVisibility( View.VISIBLE );
listView.setVisibility( View.GONE );
swipyRefreshLayout.setVisibility( View.GONE );
tvLoading.setText( message );
}
@ -121,13 +136,21 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
vh_header.bind( activity, column.access_info, column.who_account );
}
if( column.is_loading ){
if( column.bInitialLoading ){
String message = column.task_progress;
if( message == null ) message = "loading?";
showError( message );
return;
}
if( ! column.bRefreshLoading ){
swipyRefreshLayout.setRefreshing( false );
if( column.mRefreshLoadingError != null ){
Utils.showToast( activity, true, column.mRefreshLoadingError );
column.mRefreshLoadingError = null;
}
}
switch( column.type ){
default:
case Column.TYPE_TL_HOME:
@ -139,7 +162,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
showError( activity.getString( R.string.list_empty ) );
}else{
tvLoading.setVisibility( View.GONE );
listView.setVisibility( View.VISIBLE );
swipyRefreshLayout.setVisibility( View.VISIBLE );
status_adapter.set( column.status_list );
}
break;
@ -148,7 +171,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
showError( activity.getString( R.string.list_empty ) );
}else{
tvLoading.setVisibility( View.GONE );
listView.setVisibility( View.VISIBLE );
swipyRefreshLayout.setVisibility( View.VISIBLE );
status_adapter.set( column.report_list );
}
break;
@ -157,16 +180,23 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
showError( activity.getString( R.string.list_empty ) );
}else{
tvLoading.setVisibility( View.GONE );
listView.setVisibility( View.VISIBLE );
swipyRefreshLayout.setVisibility( View.VISIBLE );
status_adapter.set( column.notification_list );
}
break;
}
}
@Override
public void onRefresh( SwipyRefreshLayoutDirection direction ){
if( ! column.startRefresh( direction == SwipyRefreshLayoutDirection.BOTTOM ) ){
swipyRefreshLayout.setRefreshing( false );
}
}
///////////////////////////////////////////////////////////////////
class StatusListAdapter extends BaseAdapter {
class StatusListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
final ArrayList< Object > status_list = new ArrayList<>();
public void set( TootStatus.List src ){
@ -219,6 +249,98 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
return view;
}
@Override
public void onItemClick( AdapterView< ? > parent, View view, int position, long id ){
Object o = view.getTag();
if( o instanceof StatusViewHolder ){
( (StatusViewHolder) o ).onItemClick();
}
}
}
class HeaderViewHolder implements View.OnClickListener {
final View viewRoot;
final NetworkImageView ivBackground;
final TextView tvCreated;
final NetworkImageView ivAvatar;
final TextView tvDisplayName;
final TextView tvAcct;
final Button btnFollowing;
final Button btnFollowers;
final Button btnStatusCount;
final TextView tvNote;
TootAccount who;
public HeaderViewHolder( final ActMain activity, ListView parent ){
viewRoot = activity.getLayoutInflater().inflate( R.layout.lv_list_header, parent, false );
this.ivBackground = (NetworkImageView) viewRoot.findViewById( R.id.ivBackground );
this.tvCreated = (TextView) viewRoot.findViewById( R.id.tvCreated );
this.ivAvatar = (NetworkImageView) viewRoot.findViewById( R.id.ivAvatar );
this.tvDisplayName = (TextView) viewRoot.findViewById( R.id.tvDisplayName );
this.tvAcct = (TextView) viewRoot.findViewById( R.id.tvAcct );
this.btnFollowing = (Button) viewRoot.findViewById( R.id.btnFollowing );
this.btnFollowers = (Button) viewRoot.findViewById( R.id.btnFollowers );
this.btnStatusCount = (Button) viewRoot.findViewById( R.id.btnStatusCount );
this.tvNote = (TextView) viewRoot.findViewById( R.id.tvNote );
ivBackground.setOnClickListener( this );
btnFollowing.setOnClickListener( this );
btnFollowers.setOnClickListener( this );
btnStatusCount.setOnClickListener( this );
tvNote.setMovementMethod( LinkMovementMethod.getInstance() );
}
public void bind( ActMain activity, SavedAccount access_info, TootAccount who ){
this.who = who;
if( who == null ){
tvCreated.setText( "" );
ivBackground.setImageDrawable( null );
ivAvatar.setImageDrawable( null );
tvDisplayName.setText( "" );
tvAcct.setText( "" );
tvNote.setText( "" );
btnStatusCount.setText( activity.getString( R.string.statuses ) + "\n" + "?" );
btnFollowing.setText( activity.getString( R.string.following ) + "\n" + "?" );
btnFollowers.setText( activity.getString( R.string.followers ) + "\n" + "?" );
}else{
tvCreated.setText( TootStatus.formatTime( who.time_created_at ) );
ivBackground.setImageUrl( who.header_static, App1.getImageLoader() );
ivAvatar.setImageUrl( who.avatar_static, App1.getImageLoader() );
tvDisplayName.setText( who.display_name );
String s = access_info.getFullAcct( who );
if( who.locked ){
s += " " + Emojione.map_name2unicode.get( "lock" );
}
tvAcct.setText( Emojione.decodeEmoji( s ) );
tvNote.setText( who.note );
btnStatusCount.setText( activity.getString( R.string.statuses ) + "\n" + who.statuses_count );
btnFollowing.setText( activity.getString( R.string.following ) + "\n" + who.following_count );
btnFollowers.setText( activity.getString( R.string.followers ) + "\n" + who.followers_count );
}
}
@Override
public void onClick( View v ){
switch( v.getId() ){
case R.id.ivBackground:
if( who != null ){
activity.openBrowser( who.url );
}
break;
case R.id.btnFollowing:
Utils.showToast( activity, false, "not implemented" );
break;
case R.id.btnFollowers:
Utils.showToast( activity, false, "not implemented" );
break;
case R.id.btnStatusCount:
Utils.showToast( activity, false, "not implemented" );
break;
}
}
}
class StatusViewHolder implements View.OnClickListener {
@ -244,8 +366,8 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
final Button btnContentWarning;
final View llContents;
final TextView tvTags;
final TextView tvMentions;
final TextView tvTags;
final TextView tvMentions;
final TextView tvContent;
final View flMedia;
@ -262,14 +384,15 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
final Button btnFavourite;
final ImageButton btnMore;
TootStatus status;
SavedAccount account;
TootAccount account_thumbnail;
TootAccount account_boost;
TootAccount account_follow;
View btnConversation;
public StatusViewHolder( View view ){
this.llBoosted = view.findViewById( R.id.llBoosted );
this.ivBoosted = (ImageView) view.findViewById( R.id.ivBoosted );
this.tvBoosted = (TextView) view.findViewById( R.id.tvBoosted );
@ -279,7 +402,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
this.ivFollow = (NetworkImageView) view.findViewById( R.id.ivFollow );
this.tvFollowerName = (TextView) view.findViewById( R.id.tvFollowerName );
this.tvFollowerAcct = (TextView) view.findViewById( R.id.tvFollowerAcct );
this.btnFollow = (ImageButton)view.findViewById( R.id.btnFollow );
this.btnFollow = (ImageButton) view.findViewById( R.id.btnFollow );
this.llStatus = view.findViewById( R.id.llStatus );
@ -296,6 +419,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
this.tvTags = (TextView) view.findViewById( R.id.tvTags );
this.tvMentions = (TextView) view.findViewById( R.id.tvMentions );
this.btnConversation = view.findViewById( R.id.btnConversation );
this.btnReply = (ImageButton) view.findViewById( R.id.btnReply );
this.btnBoost = (Button) view.findViewById( R.id.btnBoost );
this.btnFavourite = (Button) view.findViewById( R.id.btnFavourite );
@ -317,13 +441,14 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
ivMedia3.setOnClickListener( this );
ivMedia4.setOnClickListener( this );
btnConversation.setOnClickListener( this );
btnReply.setOnClickListener( this );
btnBoost.setOnClickListener( this );
btnFavourite.setOnClickListener( this );
btnMore.setOnClickListener( this );
ivThumbnail.setOnClickListener( this );
tvName.setOnClickListener( this );
// ここを個別タップにすると邪魔すぎる tvName.setOnClickListener( this );
llBoosted.setOnClickListener( this );
llFollow.setOnClickListener( this );
btnFollow.setOnClickListener( this );
@ -331,6 +456,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
tvContent.setMovementMethod( LinkMovementMethod.getInstance() );
tvTags.setMovementMethod( LinkMovementMethod.getInstance() );
tvMentions.setMovementMethod( LinkMovementMethod.getInstance() );
}
public void bind( ActMain activity, View view, Object item, SavedAccount access_info ){
@ -346,21 +472,21 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
TootNotification n = (TootNotification) item;
if( TootNotification.TYPE_FAVOURITE.equals( n.type ) ){
account_boost = n.account;
llBoosted.setVisibility( View.VISIBLE );
ivBoosted.setImageResource( R.drawable.btn_favourite );
tvBoostedTime.setText( TootStatus.formatTime( n.time_created_at )
+ "\n" + access_info.getFullAcct(account_boost )
+ "\n" + access_info.getFullAcct( account_boost )
);
tvBoosted.setText( Utils.formatSpannable1( activity,R.string.display_name_favourited_by, account_boost.display_name));
tvBoosted.setText( Utils.formatSpannable1( activity, R.string.display_name_favourited_by, account_boost.display_name ) );
if( n.status != null ) bindSub( activity, view, n.status, access_info );
}else if( TootNotification.TYPE_REBLOG.equals( n.type ) ){
account_boost = n.account;
llBoosted.setVisibility( View.VISIBLE );
ivBoosted.setImageResource( R.drawable.btn_boost );
tvBoostedTime.setText( TootStatus.formatTime( n.time_created_at )
+ "\n" + access_info.getFullAcct(account_boost )
+ "\n" + access_info.getFullAcct( account_boost )
);
tvBoosted.setText( Utils.formatSpannable1( activity, R.string.display_name_boosted_by, account_boost.display_name ) );
account_boost = n.account;
@ -377,17 +503,17 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
account_follow = n.account;
llFollow.setVisibility( View.VISIBLE );
ivFollow.setImageUrl( account_follow.avatar_static, App1.getImageLoader() );
tvFollowerName.setText(account_follow.display_name );
tvFollowerName.setText( account_follow.display_name );
tvFollowerAcct.setText( access_info.getFullAcct( account_follow ) );
}else if( TootNotification.TYPE_MENTION.equals( n.type ) ){
account_boost = n.account;
llBoosted.setVisibility( View.VISIBLE );
ivBoosted.setImageResource( R.drawable.btn_reply );
tvBoostedTime.setText( TootStatus.formatTime( n.time_created_at )
+ "\n" + access_info.getFullAcct(account_boost )
+ "\n" + access_info.getFullAcct( account_boost )
);
tvBoosted.setText( Utils.formatSpannable1( activity, R.string.display_name_replied_by, account_boost.display_name ) );
if( n.status != null ) bindSub( activity, view, n.status, access_info );
}
return;
@ -414,8 +540,10 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
this.status = status;
account_thumbnail = status.account;
llStatus.setVisibility( View.VISIBLE );
tvTime.setText( TootStatus.formatTime( status.time_created_at )
+ "\n" + account.getFullAcct( status.account )
tvTime.setText(
status.id
+ " " + TootStatus.formatTime( status.time_created_at )
+ "\n" + account.getFullAcct( status.account )
);
tvName.setText( status.account.display_name );
ivThumbnail.setImageUrl( status.account.avatar_static, App1.getImageLoader() );
@ -425,14 +553,14 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
tvTags.setVisibility( View.GONE );
}else{
tvTags.setVisibility( View.VISIBLE );
tvTags.setText( status.decoded_tags);
tvTags.setText( status.decoded_tags );
}
if( status.decoded_mentions == null ){
tvMentions.setVisibility( View.GONE );
}else{
tvMentions.setVisibility( View.VISIBLE );
tvMentions.setText( status.decoded_mentions);
tvMentions.setText( status.decoded_mentions );
}
// Content warning
@ -463,14 +591,14 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
Drawable d;
int color;
if( activity.isBusyBoost( account,status )){
if( activity.isBusyBoost( account, status ) ){
color = 0xff000000;
d = ContextCompat.getDrawable( activity, R.drawable.btn_boost ).mutate();
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
btnBoost.setCompoundDrawablesRelativeWithIntrinsicBounds( d, null, null, null );
btnBoost.setText( "?" );
btnBoost.setTextColor( color );
}else{
color = ( status.reblogged ? 0xff0088ff : 0xff000000 );
d = ContextCompat.getDrawable( activity, R.drawable.btn_boost ).mutate();
@ -481,7 +609,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
}
if( activity.isBusyFav( account,status )){
if( activity.isBusyFav( account, status ) ){
color = 0xff000000;
d = ContextCompat.getDrawable( activity, R.drawable.btn_refresh ).mutate();
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
@ -546,27 +674,29 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
break;
}
case R.id.btnConversation:
activity.performConversation( account, status );
break;
case R.id.btnReply:
activity.performReply( account,status);
activity.performReply( account, status );
break;
case R.id.btnBoost:
activity.performBoost( account,status.reblog != null ? status.reblog : status ,false);
activity.performBoost( account, status, false );
break;
case R.id.btnFavourite:
activity.performFavourite( account,status.reblog != null ? status.reblog : status);
activity.performFavourite( account, status );
break;
case R.id.btnMore:
activity.performMore( account,status);
activity.performMore( account, status );
break;
case R.id.ivThumbnail:
case R.id.tvName:
activity.performOpenUser(account,account_thumbnail);
activity.performOpenUser( account, account_thumbnail );
break;
case R.id.llBoosted:
activity.performOpenUser(account,account_boost);
activity.performOpenUser( account, account_boost );
break;
case R.id.llFollow:
activity.performOpenUser(account,account_follow);
activity.performOpenUser( account, account_follow );
break;
}
}
@ -584,86 +714,10 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
ex.printStackTrace();
}
}
}
class HeaderViewHolder implements View.OnClickListener {
final View viewRoot;
final NetworkImageView ivBackground;
final TextView tvCreated;
final NetworkImageView ivAvatar;
final TextView tvDisplayName;
final TextView tvAcct;
final Button btnFollowing;
final Button btnFollowers;
final Button btnStatusCount;
final TextView tvNote;
TootAccount who;
public HeaderViewHolder( final ActMain activity, ListView parent ){
viewRoot = activity.getLayoutInflater().inflate( R.layout.lv_list_header, parent, false );
this.ivBackground = (NetworkImageView) viewRoot.findViewById( R.id.ivBackground );
this.tvCreated = (TextView) viewRoot.findViewById( R.id.tvCreated );
this.ivAvatar = (NetworkImageView) viewRoot.findViewById( R.id.ivAvatar );
this.tvDisplayName = (TextView) viewRoot.findViewById( R.id.tvDisplayName );
this.tvAcct = (TextView) viewRoot.findViewById( R.id.tvAcct );
this.btnFollowing = (Button) viewRoot.findViewById( R.id.btnFollowing );
this.btnFollowers = (Button) viewRoot.findViewById( R.id.btnFollowers );
this.btnStatusCount = (Button) viewRoot.findViewById( R.id.btnStatusCount );
this.tvNote = (TextView) viewRoot.findViewById( R.id.tvNote );
ivBackground.setOnClickListener( this );
btnFollowing.setOnClickListener( this );
btnFollowers.setOnClickListener( this );
btnStatusCount.setOnClickListener( this );
tvNote.setMovementMethod( LinkMovementMethod.getInstance() );
}
public void bind( ActMain activity, SavedAccount access_info, TootAccount who ){
this.who = who;
if( who == null ){
tvCreated.setText( "" );
ivBackground.setImageDrawable( null );
ivAvatar.setImageDrawable( null );
tvDisplayName.setText( "" );
tvAcct.setText( "" );
tvNote.setText( "" );
btnStatusCount.setText( activity.getString( R.string.statuses ) + "\n" + "?" );
btnFollowing.setText( activity.getString( R.string.following ) + "\n" + "?" );
btnFollowers.setText( activity.getString( R.string.followers ) + "\n" + "?" );
}else{
tvCreated.setText( TootStatus.formatTime( who.time_created_at ) );
ivBackground.setImageUrl( who.header_static, App1.getImageLoader() );
ivAvatar.setImageUrl( who.avatar_static, App1.getImageLoader() );
tvDisplayName.setText( who.display_name );
tvAcct.setText( access_info.getFullAcct( who ) );
tvNote.setText( who.note );
btnStatusCount.setText( activity.getString( R.string.statuses ) + "\n" + who.statuses_count );
btnFollowing.setText( activity.getString( R.string.following ) + "\n" + who.following_count );
btnFollowers.setText( activity.getString( R.string.followers ) + "\n" + who.followers_count );
}
}
@Override
public void onClick( View v ){
switch( v.getId() ){
case R.id.ivBackground:
if( who != null ){
activity.openBrowser( who.url );
}
break;
case R.id.btnFollowing:
Utils.showToast( activity, false, "not implemented" );
break;
case R.id.btnFollowers:
Utils.showToast( activity, false, "not implemented" );
break;
case R.id.btnStatusCount:
Utils.showToast( activity, false, "not implemented" );
break;
}
public void onItemClick(){
activity.performConversation( account, status );
}
}
}

View File

@ -0,0 +1,16 @@
package jp.juggler.subwaytooter;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class Pref {
public static SharedPreferences pref(Context context){
return PreferenceManager.getDefaultSharedPreferences( context );
}
public static final String KEY_BACK_TO_COLUMN_LIST ="BackToColumnList";
public static final String KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN ="DontConfirmBeforeCloseColumn";
}

View File

@ -69,8 +69,17 @@ public class TootApiClient {
}
public TootApiResult request( String path, Request.Builder request_builder ){
JSONObject client_info = null;
log.d("request: %s",path);
TootApiResult result = request_sub( path,request_builder );
if( result.error != null ){
log.d("error: %s",result.error);
}
return result;
}
public TootApiResult request_sub( String path, Request.Builder request_builder ){
JSONObject client_info = null;
JSONObject token_info = ( account == null ? null : account.token_info );
for( ; ; ){
@ -84,7 +93,7 @@ public class TootApiClient {
callback.publishApiProgress( context.getString( R.string.register_app_to_server, instance ) );
// OAuth2 クライアント登録
String client_name = "jp.juggler.subwaytooter." + UUID.randomUUID().toString();
String client_name = "SubwayTooter" ; // + UUID.randomUUID().toString();
Request request = new Request.Builder()
.url( "https://" + instance + "/api/v1/apps" )
@ -218,7 +227,7 @@ public class TootApiClient {
return new TootApiResult( context.getString( R.string.response_not_json ) + "\n" + json );
}else if( json.startsWith( "[" ) ){
JSONArray array = new JSONArray( json );
return new TootApiResult( token_info, json, array );
return new TootApiResult( response,token_info, json, array );
}else{
JSONObject object = new JSONObject( json );
@ -226,7 +235,7 @@ public class TootApiClient {
if( ! TextUtils.isEmpty( error ) ){
return new TootApiResult( context.getString( R.string.api_error, error ) );
}
return new TootApiResult( token_info, json, object );
return new TootApiResult( response,token_info, json, object );
}
}catch( Throwable ex ){
ex.printStackTrace();

View File

@ -3,26 +3,32 @@ package jp.juggler.subwaytooter.api;
import org.json.JSONArray;
import org.json.JSONObject;
import okhttp3.Response;
public class TootApiResult {
public String error;
public JSONObject object;
public JSONArray array;
public String json;
public JSONObject token_info;
public Response response;
public TootApiResult( String error ){
this.error = error;
}
public TootApiResult( JSONObject token_info,String json,JSONObject object ){
public TootApiResult( Response response,JSONObject token_info,String json,JSONObject object ){
this.token_info = token_info;
this.json = json;
this.object = object;
this.response = response;
}
public TootApiResult(JSONObject token_info, String json,JSONArray array ){
public TootApiResult( Response response, JSONObject token_info, String json, JSONArray array ){
this.token_info = token_info;
this.json = json;
this.array = array;
this.response = response;
}
}

View File

@ -0,0 +1,10 @@
package jp.juggler.subwaytooter.api.entity;
/**
* Created by tateisu on 2017/04/23.
*/
public class TootId {
public long id;
}

View File

@ -8,10 +8,10 @@ import java.util.ArrayList;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class TootNotification {
public class TootNotification extends TootId{
// The notification ID
public long id;
//TootId public long id;
// One of: "mention", "reblog", "favourite", "follow"
public String type;

View File

@ -8,10 +8,10 @@ import java.util.ArrayList;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class TootReport {
public class TootReport extends TootId{
// The ID of the report
public long id;
//TootId public long id;
// The action taken in response to the report
public String action_taken;

View File

@ -17,7 +17,7 @@ import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class TootStatus {
public class TootStatus extends TootId{
public static class List extends ArrayList< TootStatus > {
@ -25,7 +25,7 @@ public class TootStatus {
}
// The ID of the status
public long id;
// TootId public long id;
// A Fediverse-unique resource ID
public String uri;
@ -94,12 +94,18 @@ public class TootStatus {
public Spannable decoded_tags;
public Spannable decoded_mentions;
public JSONObject json;
public boolean conversation_main;
public static TootStatus parse( LogCategory log, JSONObject src ){
if( src == null ) return null;
try{
TootStatus status = new TootStatus();
status.json = src;
// log.d( "parse: %s", src.toString() );
status.id = src.optLong( "id" );
status.uri = Utils.optStringX( src, "uri" );
@ -170,4 +176,25 @@ public class TootStatus {
return date_format.format( new Date( t ) );
}
// 公開範囲を比較する
// 公開範囲が広い => 大きい
// aの方が小さい狭い)ならマイナス
// aの方が大きい狭い)ならプラス
// IndexOutOfBoundsException 公開範囲が想定外
public static int compareVisibility( String a, String b ){
int ia = compareVisibility_tmp(a);
int ib = compareVisibility_tmp(b);
if( ia < ib ) return -1;
if( ia > ib ) return 1;
return 0;
}
private static int compareVisibility_tmp( String a ){
if(TootStatus.VISIBILITY_DIRECT.equals( a ) ) return 0;
if(TootStatus.VISIBILITY_PRIVATE.equals( a ) ) return 1;
if(TootStatus.VISIBILITY_UNLISTED.equals( a ) ) return 2;
if(TootStatus.VISIBILITY_PUBLIC.equals( a ) ) return 3;
throw new IndexOutOfBoundsException( "visibility not in range" );
}
}

View File

@ -10,6 +10,7 @@ import java.util.Comparator;
import jp.juggler.subwaytooter.ActMain;
import jp.juggler.subwaytooter.R;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.Utils;
public class AccountPicker {
@ -21,12 +22,20 @@ public class AccountPicker {
final ArrayList<SavedAccount > account_list = SavedAccount.loadAccountList(ActMain.log);
if( account_list == null || account_list.isEmpty() ){
Utils.showToast(activity,false,R.string.account_empty);
return;
}
if( account_list.size() == 1 ){
callback.onAccountPicked(account_list.get(0));
return;
}
Collections.sort( account_list, new Comparator< SavedAccount >() {
@Override
public int compare( SavedAccount o1, SavedAccount o2 ){
int i = String.CASE_INSENSITIVE_ORDER.compare( o1.acct, o2.acct );
if( i != 0 ) return i;
return String.CASE_INSENSITIVE_ORDER.compare( o1.host, o2.host );
return String.CASE_INSENSITIVE_ORDER.compare( o1.user, o2.user );
}
} );
@ -34,7 +43,7 @@ public class AccountPicker {
for(int i=0,ie=account_list.size();i<ie;++i){
SavedAccount ai = account_list.get(i);
caption_list[i] = ai.acct;
caption_list[i] = ai.user;
}
new AlertDialog.Builder(activity)

View File

@ -1,6 +1,8 @@
package jp.juggler.subwaytooter.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.text.InputType;
import android.text.TextUtils;
import android.view.View;
import android.view.WindowManager;
@ -27,10 +29,18 @@ public class LoginForm {
void startLogin(Dialog dialog,String instance,String user_main,String password);
}
public static void showLoginForm(final ActMain activity,final LoginFormCallback callback){
public static void showLoginForm( final Activity activity, String instance , final LoginFormCallback callback){
final View view = activity.getLayoutInflater().inflate( R.layout.dlg_account_add, null, false );
final AutoCompleteTextView etInstance = (AutoCompleteTextView) view.findViewById( R.id.etInstance );
final EditText etUserMail = (EditText) view.findViewById( R.id.etUserMail );
if( !TextUtils.isEmpty( instance ) ){
etInstance.setText(instance);
etInstance.setInputType( InputType.TYPE_NULL );
etInstance.setEnabled( false );
etInstance.setFocusable( false );
}
final EditText etUserPassword = (EditText) view.findViewById( R.id.etUserPassword );
final Dialog dialog = new Dialog( activity );
dialog.setContentView( view );

View File

@ -50,17 +50,17 @@ public class LogData {
public static long insert( ContentValues cv, long time, int level,String category, String message ){
Log.d( TAG,category+": "+message);
try{
cv.clear();
cv.put( COL_TIME, time );
cv.put( COL_LEVEL, level );
cv.put( COL_MESSAGE, message );
cv.put( COL_CATEGORY, category );
return App1.getDB().insert( table, null, cv );
}catch( Throwable ex ){
ex.printStackTrace();
return - 1L;
}
// try{
// cv.clear();
// cv.put( COL_TIME, time );
// cv.put( COL_LEVEL, level );
// cv.put( COL_MESSAGE, message );
// cv.put( COL_CATEGORY, category );
// return App1.getDB().insert( table, null, cv );
// }catch( Throwable ex ){
// ex.printStackTrace();
// }
return -1L;
}
public static String getLogLevelString( int level ){

View File

@ -105,11 +105,14 @@ public class SavedAccount extends TootAccount{
public void updateTokenInfo( JSONObject token_info ){
if( db_id != INVALID_ID ){
this.token_info = token_info;
ContentValues cv = new ContentValues();
cv.put( COL_TOKEN, token_info.toString() );
App1.getDB().update( table, cv, COL_ID + "=?", new String[]{ Long.toString(db_id) } );
}
}
public void saveSetting(){
if( db_id != INVALID_ID ){
ContentValues cv = new ContentValues();
@ -120,9 +123,9 @@ public class SavedAccount extends TootAccount{
}
}
public static SavedAccount loadAccount( LogCategory log, long id ){
public static SavedAccount loadAccount( LogCategory log, long db_id ){
try{
Cursor cursor = App1.getDB().query( table, null, COL_ID+"=?", new String[]{ Long.toString(id) }, null, null, null );
Cursor cursor = App1.getDB().query( table, null, COL_ID+"=?", new String[]{ Long.toString(db_id) }, null, null, null );
try{
if( cursor.moveToFirst() ){
return parse( cursor );
@ -156,12 +159,15 @@ public class SavedAccount extends TootAccount{
}
public String getFullAcct(TootAccount who ){
if( who== null || who.acct ==null ) return "@?";
if( -1 != who.acct.indexOf( '@' ) ){
return "@" + who.acct;
}else{
return "@"+ who.acct +"@"+ this.host;
}
return who==null ? "@?" : getFullAcct( who.acct );
}
public String getFullAcct( String acct ){
if( acct ==null ) return "@?";
if( -1 != acct.indexOf( '@' ) ){
return "@" + acct;
}else{
return "@"+ acct +"@"+ this.host;
}
}
}

View File

@ -14,8 +14,8 @@ public abstract class Emojione
{
private static final Pattern SHORTNAME_PATTERN = Pattern.compile(":([-+\\w]+):");
private static final HashMap<String,String> map_name2unicode = EmojiMap._shortNameToUnicode;
private static final HashMap<String,String> map_unicode2name = EmojiMap._unicodeToShortName;
public static final HashMap<String,String> map_name2unicode = EmojiMap._shortNameToUnicode;
public static final HashMap<String,String> map_unicode2name = EmojiMap._unicodeToShortName;
static class DecodeEnv{
SpannableStringBuilder sb = new SpannableStringBuilder();

View File

@ -1,6 +1,7 @@
package jp.juggler.subwaytooter.util;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import java.io.ByteArrayInputStream;
@ -15,6 +16,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Environment;
@ -31,6 +33,7 @@ import android.util.SparseBooleanArray;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
@ -797,4 +800,16 @@ public class Utils {
return null;
}
public static Activity getActivityFromView( View view) {
Context context = view.getContext();
while (context instanceof ContextWrapper ) {
if (context instanceof Activity) {
return (Activity)context;
}
context = ((ContextWrapper)context).getBaseContext();
}
return null;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="@color/list_item_bg_pressed_dragged"/>
</shape>
</item>
<item>
<shape>
<solid android:color="@android:color/transparent"/>
</shape>
</item>
</selector>

View File

@ -3,17 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:fillViewport="true"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:scrollbarStyle="outsideOverlay"
>
@ -21,6 +11,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp"
android:paddingBottom="48dp"
>
<View style="@style/setting_divider"/>
@ -39,6 +31,7 @@
/>
</LinearLayout>
<View style="@style/setting_divider"/>
<TextView
@ -55,6 +48,7 @@
/>
</LinearLayout>
<View style="@style/setting_divider"/>
<TextView
@ -68,10 +62,11 @@
android:id="@+id/btnAccessToken"
style="@style/setting_horizontal_stretch"
android:ellipsize="start"
android:text="@string/get_access_token"
android:text="@string/update_access_token"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button
@ -137,264 +132,264 @@
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etTargetUrl"-->
<!--android:text="@string/target_url"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etTargetUrl"-->
<!--android:text="@string/target_url"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etTargetUrl"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="textUri"-->
<!--/>-->
<!--<EditText-->
<!--android:id="@+id/etTargetUrl"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="textUri"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnHelpTargetUrl"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnHelpTargetUrl"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/local_folder"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/local_folder"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<TextView-->
<!--android:id="@+id/tvFolder"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:ellipsize="start"-->
<!--/>-->
<!--<TextView-->
<!--android:id="@+id/tvFolder"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:ellipsize="start"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnFolderPicker"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:minWidth="32dp"-->
<!--android:text="@string/dot_dot"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnFolderPicker"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:minWidth="32dp"-->
<!--android:text="@string/dot_dot"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnHelpFolderPicker"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnHelpFolderPicker"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etFileType"-->
<!--android:text="@string/file_type"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etFileType"-->
<!--android:text="@string/file_type"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etFileType"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="text"-->
<!--/>-->
<!--<EditText-->
<!--android:id="@+id/etFileType"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="text"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnFileTypeHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnFileTypeHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etRepeatInterval"-->
<!--android:text="@string/repeat_interval"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etRepeatInterval"-->
<!--android:text="@string/repeat_interval"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etRepeatInterval"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<EditText-->
<!--android:id="@+id/etRepeatInterval"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnIntervalHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnIntervalHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/geo_tagging_mode"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/geo_tagging_mode"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Spinner-->
<!--android:id="@+id/spLocationMode"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--/>-->
<!--<Spinner-->
<!--android:id="@+id/spLocationMode"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationModeHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationModeHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etLocationIntervalDesired"-->
<!--android:text="@string/location_interval_desired"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etLocationIntervalDesired"-->
<!--android:text="@string/location_interval_desired"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etLocationIntervalDesired"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<EditText-->
<!--android:id="@+id/etLocationIntervalDesired"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationIntervalDesiredHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationIntervalDesiredHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etLocationIntervalMin"-->
<!--android:text="@string/location_interval_min"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etLocationIntervalMin"-->
<!--android:text="@string/location_interval_min"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etLocationIntervalMin"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<EditText-->
<!--android:id="@+id/etLocationIntervalMin"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationIntervalMinHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationIntervalMinHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/force_wifi_ap"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/force_wifi_ap"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swForceWifi"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Switch-->
<!--android:id="@+id/swForceWifi"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnForceWifiHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnForceWifiHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etSSID"-->
<!--android:text="@string/wifi_ap_ssid"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etSSID"-->
<!--android:text="@string/wifi_ap_ssid"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etSSID"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="text"-->
<!--/>-->
<!--<EditText-->
<!--android:id="@+id/etSSID"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="text"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnSSIDPicker"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:minWidth="32dp"-->
<!--android:text="@string/dot_dot"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnSSIDPicker"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:minWidth="32dp"-->
<!--android:text="@string/dot_dot"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnSSIDHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnSSIDHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/thumbnail_auto_rotate"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/thumbnail_auto_rotate"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swThumbnailAutoRotate"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Switch-->
<!--android:id="@+id/swThumbnailAutoRotate"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnThumbnailAutoRotateHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnThumbnailAutoRotateHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/copy_before_view_send"-->
<!--/>-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/copy_before_view_send"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swCopyBeforeViewSend"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Switch-->
<!--android:id="@+id/swCopyBeforeViewSend"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnCopyBeforeViewSendHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnCopyBeforeViewSendHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_marginBottom="20dp"-->
<!--android:layout_marginTop="20dp"-->
<!--android:gravity="center"-->
<!--android:text="@string/setting_desc"-->
<!--/>-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_marginBottom="20dp"-->
<!--android:layout_marginTop="20dp"-->
<!--android:gravity="center"-->
<!--android:text="@string/setting_desc"-->
<!--/>-->
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,401 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:fillViewport="true"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:scrollbarStyle="outsideOverlay"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<View style="@style/setting_divider"/>
<TextView
style="@style/setting_row_label"
android:text="@string/back_to_column_list"
/>
<LinearLayout style="@style/setting_row_form">
<Switch
android:id="@+id/swBackToColumnList"
style="@style/setting_horizontal_stretch"
android:gravity="center"
/>
</LinearLayout>
<View style="@style/setting_divider"/>
<TextView
style="@style/setting_row_label"
android:text="@string/dont_confirm_before_close_column"
/>
<LinearLayout style="@style/setting_row_form">
<Switch
android:id="@+id/swDontConfirmBeforeCloseColumn"
style="@style/setting_horizontal_stretch"
android:gravity="center"
/>
</LinearLayout>
<View style="@style/setting_divider"/>
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/actions"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Button-->
<!--android:id="@+id/btnAccessToken"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:ellipsize="start"-->
<!--android:text="@string/update_access_token"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Button-->
<!--android:id="@+id/btnAccountRemove"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:ellipsize="start"-->
<!--android:text="@string/account_remove"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/default_status_visibility"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Button-->
<!--android:id="@+id/btnVisibility"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/confirm_before_boost"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swConfirmBeforeBoost"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/sensitive_content_default_open"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swNSFWOpen"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etTargetUrl"-->
<!--android:text="@string/target_url"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etTargetUrl"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="textUri"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnHelpTargetUrl"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/local_folder"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<TextView-->
<!--android:id="@+id/tvFolder"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:ellipsize="start"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnFolderPicker"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:minWidth="32dp"-->
<!--android:text="@string/dot_dot"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnHelpFolderPicker"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etFileType"-->
<!--android:text="@string/file_type"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etFileType"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="text"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnFileTypeHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etRepeatInterval"-->
<!--android:text="@string/repeat_interval"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etRepeatInterval"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnIntervalHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/geo_tagging_mode"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Spinner-->
<!--android:id="@+id/spLocationMode"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationModeHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etLocationIntervalDesired"-->
<!--android:text="@string/location_interval_desired"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etLocationIntervalDesired"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationIntervalDesiredHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etLocationIntervalMin"-->
<!--android:text="@string/location_interval_min"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etLocationIntervalMin"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="number"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnLocationIntervalMinHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/force_wifi_ap"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swForceWifi"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnForceWifiHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:labelFor="@+id/etSSID"-->
<!--android:text="@string/wifi_ap_ssid"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<EditText-->
<!--android:id="@+id/etSSID"-->
<!--style="@style/setting_edit_text"-->
<!--android:inputType="text"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnSSIDPicker"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:minWidth="32dp"-->
<!--android:text="@string/dot_dot"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnSSIDHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/thumbnail_auto_rotate"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swThumbnailAutoRotate"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnThumbnailAutoRotateHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--style="@style/setting_row_label"-->
<!--android:text="@string/copy_before_view_send"-->
<!--/>-->
<!--<LinearLayout style="@style/setting_row_form">-->
<!--<Switch-->
<!--android:id="@+id/swCopyBeforeViewSend"-->
<!--style="@style/setting_horizontal_stretch"-->
<!--android:gravity="center"-->
<!--/>-->
<!--<Button-->
<!--android:id="@+id/btnCopyBeforeViewSendHelp"-->
<!--style="@style/setting_row_help"-->
<!--/>-->
<!--</LinearLayout>-->
<!--<View style="@style/setting_divider"/>-->
<!--<TextView-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_marginBottom="20dp"-->
<!--android:layout_marginTop="20dp"-->
<!--android:gravity="center"-->
<!--android:text="@string/setting_desc"-->
<!--/>-->
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<!-- Need to wrap DragListView in another layout for wrap_content to work for some reason -->
<com.woxthebox.draglistview.DragListView
android:id="@+id/drag_list_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:textSize="12sp"
android:text="@string/column_list_desc"
android:gravity="center"
/>
</LinearLayout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="false"
android:fadeScrollbars="false"
android:paddingBottom="64dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:scrollbarStyle="outsideOverlay"
/>
</LinearLayout>

View File

@ -7,13 +7,62 @@
>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="12dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#cccccc"
android:orientation="vertical"
android:padding="6dp"
android:id="@+id/llReply"
android:layout_marginBottom="12dp"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/reply_to_this_status"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
>
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/ivReply"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/btn_bg_transparent"
android:scaleType="fitCenter"
/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/tvReplyTo"
android:gravity="center_vertical"
/>
<ImageButton
android:id="@+id/btnRemoveReply"
android:layout_marginStart="4dp"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/btn_bg_transparent"
android:gravity="center_vertical"
android:src="@drawable/black_close"
/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<com.woxthebox.draglistview.swipe.ListSwipeItem
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:leftViewId="@+id/item_left"
app:rightViewId="@+id/item_right"
app:swipeViewId="@+id/item_layout">
<TextView
android:id="@+id/item_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@+id/item_layout"
android:layout_alignTop="@+id/item_layout"
android:background="#0088ff"
android:gravity="start|center_vertical"
android:padding="16dp"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="20sp"/>
<TextView
android:id="@+id/item_right"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@+id/item_layout"
android:layout_alignTop="@+id/item_layout"
android:background="#FF0000"
android:gravity="end|center_vertical"
android:padding="16dp"
android:text="@string/delete"
android:textColor="@android:color/white"
android:textSize="20sp"/>
<LinearLayout
android:id="@id/item_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_item_selector"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="16dp">
<!-- minHeight の代わりにViewで賄う -->
<View
android:layout_width="0dp"
android:layout_height="48dp"
/>
<ImageView
android:id="@+id/ivDragHandle"
android:layout_width="48dp"
android:layout_height="match_parent"
android:background="#80cccccc"
android:contentDescription="@string/drag_handle"
android:scaleType="center"
android:src="@drawable/ic_knob"
/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView
android:id="@+id/tvAccess"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:textColor="#888888"
android:textSize="12sp"
/>
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
<ImageView
android:id="@+id/ivBookmark"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="12dp"
android:layout_marginStart="8dp"
android:contentDescription="@string/last_selection"
android:scaleType="center"
android:src="@drawable/ic_bookmark"
/>
</LinearLayout>
</com.woxthebox.draglistview.swipe.ListSwipeItem>

View File

@ -63,6 +63,7 @@
android:gravity="center"
tools:text="\@fugahogehogera\@jugemujyugemugokounosurikire.jp"
android:textColor="@color/colorLink"
android:drawablePadding="4dp"
/>

View File

@ -155,7 +155,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/btn_bg_transparent"
android:textStyle="bold"
tools:text="Displayname @username"
/>
@ -298,6 +297,17 @@
android:layout_marginTop="8dp"
android:orientation="horizontal"
>
<ImageButton
android:id="@+id/btnConversation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/btn_bg_transparent"
android:contentDescription="@string/conversation_view"
android:minWidth="48dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:src="@drawable/ic_conversation"
/>
<ImageButton
android:id="@+id/btnReply"
@ -306,6 +316,8 @@
android:background="@drawable/btn_bg_transparent"
android:contentDescription="@string/reply"
android:minWidth="48dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:src="@drawable/btn_reply"
/>
@ -315,6 +327,8 @@
android:layout_height="match_parent"
android:background="@drawable/btn_bg_transparent"
android:drawablePadding="4dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:minWidth="48dp"
/>
@ -324,6 +338,8 @@
android:layout_height="match_parent"
android:background="@drawable/btn_bg_transparent"
android:drawablePadding="4dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:minWidth="48dp"
/>
@ -334,6 +350,8 @@
android:background="@drawable/btn_bg_transparent"
android:contentDescription="@string/more"
android:minWidth="48dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:src="@drawable/btn_more"
/>

View File

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<LinearLayout
@ -11,25 +12,49 @@
android:layout_height="wrap_content"
android:background="#ddd"
android:orientation="vertical"
android:padding="12dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
>
<TextView
android:id="@+id/tvColumnContext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:textColor="#888"
android:textSize="12sp"
tools:text="tvColumnContext"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:id="@+id/tvColumnContext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:textColor="#888"
android:textSize="12sp"
tools:text="tvColumnContext"
/>
<TextView
android:id="@+id/tvColumnIndex"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:gravity="end"
android:textColor="#888"
android:textSize="12sp"
tools:text="col 6/12"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:orientation="horizontal"
android:gravity="center_vertical"
>
<TextView
android:id="@+id/tvColumnName"
android:layout_width="0dp"
@ -62,6 +87,8 @@
</LinearLayout>
</LinearLayout>
<TextView
@ -72,17 +99,27 @@
android:gravity="center"
/>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
<com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout
android:id="@+id/swipyRefreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srl_direction="both">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="false"
android:fadeScrollbars="false"
android:paddingBottom="64dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:scrollbarStyle="outsideOverlay"
/>
</com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout>
android:clipToPadding="false"
android:fadeScrollbars="false"
android:paddingBottom="64dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:scrollbarStyle="outsideOverlay"
/>
</LinearLayout>

View File

@ -16,12 +16,22 @@
android:title="@string/account_setting"/>
</menu>
</item>
<item android:title="@string/add_column">
<item android:title="@string/column">
<menu>
<item
android:id="@+id/nav_column_list"
android:icon="@drawable/ic_list"
android:title="@string/column_list"/>
<item
android:id="@+id/nav_add_tl_home"
android:icon="@drawable/btn_home"
android:title="@string/home"/>
<item
android:id="@+id/nav_add_notifications"
android:icon="@drawable/btn_notification"
android:title="@string/notifications"/>
<item
android:id="@+id/nav_add_tl_local"
android:icon="@drawable/btn_local_tl"
@ -31,20 +41,15 @@
android:icon="@drawable/btn_federate_tl"
android:title="@string/federate_tl"/>
<item
android:id="@+id/nav_add_statuses"
android:icon="@drawable/btn_statuses"
android:title="@string/your_statuses"/>
<item
android:id="@+id/nav_add_favourites"
android:icon="@drawable/btn_favourite"
android:title="@string/your_favourites"/>
<item
android:id="@+id/nav_add_notifications"
android:icon="@drawable/btn_notification"
android:title="@string/your_notifications"/>
android:id="@+id/nav_add_statuses"
android:icon="@drawable/btn_statuses"
android:title="@string/your_statuses"/>
<!--<item-->
<!--android:id="@+id/nav_add_reports"-->
@ -64,10 +69,6 @@
</item>
<item android:title="@string/setting">
<menu>
<item
android:id="@+id/nav_column_list"
android:icon="@drawable/ic_list"
android:title="@string/column_list"/>
<item
android:id="@+id/nav_app_setting"

View File

@ -5,5 +5,5 @@
<color name="colorAccent">#5a5a5a</color>
<color name="colorLink">#00a2ff</color>
<color name="list_item_bg_pressed_dragged">#AACCCCCC</color>
</resources>

View File

@ -1,8 +1,8 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="nav_header_vertical_spacing">16dp</dimen>
<dimen name="activity_horizontal_margin">12dp</dimen>
<dimen name="activity_vertical_margin">12dp</dimen>
<dimen name="nav_header_vertical_spacing">12dp</dimen>
<dimen name="nav_header_height">160dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="fab_margin">12dp</dimen>
</resources>

View File

@ -102,9 +102,8 @@
<string name="account_setting">account setting</string>
<string name="setting">setting</string>
<string name="app_setting">app setting</string>
<string name="columun_list">column list</string>
<string name="column_list">column list</string>
<string name="get_access_token">get access token</string>
<string name="update_access_token">update access token</string>
<string name="account_remove">remove account from this app</string>
<string name="actions">actions</string>
<string name="default_status_visibility">default visibility of status</string>
@ -112,4 +111,22 @@
<string name="sensitive_content_default_open">sensitive content default open</string>
<string name="user">user</string>
<string name="confirm_account_remove">Account will be deleted. also all columns are remove.\nAre you sure?</string>
<string name="user_name_not_match">user name not match.</string>
<string name="access_token_updated">access token updated.</string>
<string name="back_to_column_list">show column list when back button pressed</string>
<string name="account_empty">Account is not set up.Please add account.</string>
<string name="confirm_before_close_column">confirm before close column</string>
<string name="dont_confirm_before_close_column">don\'t confirm before close column</string>
<string name="drag_handle">drag handle</string>
<string name="delete">delete</string>
<string name="column_list_desc">swipe to delete. sorting and deletion is applied at back or pick.</string>
<string name="last_selection">last selection</string>
<string name="column_index">%1$d/%2$d</string>
<string name="reply_to_this_post">reply to this post:</string>
<string name="reply_to_this_status">reply to this status:</string>
<string name="cant_change_account_when_attachiment_specified">can\'t change account when attachement specified.</string>
<string name="column">column</string>
<string name="conversation_view">conversation view</string>
<string name="conversation">status:%1$d</string>
<string name="conversation_around">conversation around status:%1$d</string>
</resources>

View File

@ -3,6 +3,7 @@
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'