v0.6.4
This commit is contained in:
parent
57b6ff7283
commit
c6f393ac87
|
@ -78,6 +78,8 @@ public class ActAppSetting extends AppCompatActivity
|
|||
Switch swDontRefreshOnResume;
|
||||
Switch swDontScreenOff;
|
||||
Switch swDisableTabletMode;
|
||||
Switch swDontCropMediaThumb;
|
||||
|
||||
|
||||
Spinner spBackButtonAction;
|
||||
Spinner spUITheme;
|
||||
|
@ -147,6 +149,10 @@ public class ActAppSetting extends AppCompatActivity
|
|||
swDisableTabletMode = (Switch) findViewById( R.id.swDisableTabletMode );
|
||||
swDisableTabletMode.setOnCheckedChangeListener( this );
|
||||
|
||||
swDontCropMediaThumb = (Switch) findViewById( R.id.swDontCropMediaThumb );
|
||||
swDontCropMediaThumb.setOnCheckedChangeListener( this );
|
||||
|
||||
|
||||
cbNotificationSound = (CheckBox) findViewById( R.id.cbNotificationSound );
|
||||
cbNotificationVibration = (CheckBox) findViewById( R.id.cbNotificationVibration );
|
||||
cbNotificationLED = (CheckBox) findViewById( R.id.cbNotificationLED );
|
||||
|
@ -255,6 +261,7 @@ public class ActAppSetting extends AppCompatActivity
|
|||
swDontRefreshOnResume.setChecked( pref.getBoolean( Pref.KEY_DONT_REFRESH_ON_RESUME, false ) );
|
||||
swDontScreenOff.setChecked( pref.getBoolean( Pref.KEY_DONT_SCREEN_OFF, false ) );
|
||||
swDisableTabletMode.setChecked( pref.getBoolean( Pref.KEY_DISABLE_TABLET_MODE, false ) );
|
||||
swDontCropMediaThumb.setChecked( pref.getBoolean( Pref.KEY_DONT_CROP_MEDIA_THUMBNAIL, false ) );
|
||||
|
||||
cbNotificationSound.setChecked( pref.getBoolean( Pref.KEY_NOTIFICATION_SOUND, true ) );
|
||||
cbNotificationVibration.setChecked( pref.getBoolean( Pref.KEY_NOTIFICATION_VIBRATION, true ) );
|
||||
|
@ -295,6 +302,8 @@ public class ActAppSetting extends AppCompatActivity
|
|||
.putBoolean( Pref.KEY_DONT_REFRESH_ON_RESUME, swDontRefreshOnResume.isChecked() )
|
||||
.putBoolean( Pref.KEY_DONT_SCREEN_OFF, swDontScreenOff.isChecked() )
|
||||
.putBoolean( Pref.KEY_DISABLE_TABLET_MODE, swDisableTabletMode.isChecked() )
|
||||
.putBoolean( Pref.KEY_DONT_CROP_MEDIA_THUMBNAIL, swDontCropMediaThumb.isChecked() )
|
||||
|
||||
|
||||
.putBoolean( Pref.KEY_NOTIFICATION_SOUND, cbNotificationSound.isChecked() )
|
||||
.putBoolean( Pref.KEY_NOTIFICATION_VIBRATION, cbNotificationVibration.isChecked() )
|
||||
|
|
|
@ -626,11 +626,15 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
Typeface timeline_font;
|
||||
|
||||
boolean dont_crop_media_thumbnail;
|
||||
|
||||
void initUI(){
|
||||
setContentView( R.layout.act_main );
|
||||
|
||||
String sv = pref.getString( Pref.KEY_TIMELINE_FONT, "" );
|
||||
|
||||
dont_crop_media_thumbnail = pref.getBoolean( Pref.KEY_DONT_CROP_MEDIA_THUMBNAIL,false );
|
||||
|
||||
if( ! TextUtils.isEmpty( sv ) ){
|
||||
try{
|
||||
timeline_font = Typeface.createFromFile( sv );
|
||||
|
|
|
@ -394,7 +394,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
|
||||
// リプライ表示をつける
|
||||
in_reply_to_id = reply_status.id;
|
||||
in_reply_to_text = reply_status.content;
|
||||
in_reply_to_text = reply_status.decoded_content.toString();
|
||||
in_reply_to_image = reply_status.account.avatar_static;
|
||||
|
||||
// 公開範囲
|
||||
|
@ -1466,7 +1466,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
llReply.setVisibility( View.GONE );
|
||||
}else{
|
||||
llReply.setVisibility( View.VISIBLE );
|
||||
tvReplyTo.setText( HTMLDecoder.decodeHTML( account, in_reply_to_text ) );
|
||||
tvReplyTo.setText( HTMLDecoder.decodeHTML( account ,in_reply_to_text ,true,null ));
|
||||
ivReply.setCornerRadius( pref, 16f );
|
||||
ivReply.setImageUrl( in_reply_to_image );
|
||||
|
||||
|
|
|
@ -44,11 +44,11 @@ public class ActText extends AppCompatActivity implements View.OnClickListener {
|
|||
if( ! TextUtils.isEmpty( status.spoiler_text ) ){
|
||||
sb.append( context.getString( R.string.send_header_content_warning ) );
|
||||
sb.append( ": " );
|
||||
sb.append( HTMLDecoder.decodeHTMLForClipboard( access_info, status.spoiler_text ) );
|
||||
sb.append( HTMLDecoder.decodeHTML( access_info, status.spoiler_text ,false,null) );
|
||||
sb.append( "\n" );
|
||||
}
|
||||
sb.append( "\n" );
|
||||
sb.append( HTMLDecoder.decodeHTMLForClipboard( access_info, status.content ) );
|
||||
sb.append( HTMLDecoder.decodeHTML( access_info, status.content ,false,null) );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.content.SharedPreferences;
|
|||
import android.net.Uri;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
@ -37,6 +38,7 @@ import jp.juggler.subwaytooter.table.NotificationTracking;
|
|||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree;
|
||||
|
||||
public class AlarmService extends IntentService {
|
||||
|
||||
|
@ -105,7 +107,7 @@ public class AlarmService extends IntentService {
|
|||
|
||||
// クラッシュレポートによると App1.onCreate より前にここを通る場合がある
|
||||
// データベースへアクセスできるようにする
|
||||
App1.prepareDB(this.getApplicationContext());
|
||||
App1.prepareDB( this.getApplicationContext() );
|
||||
|
||||
if( intent != null ){
|
||||
String action = intent.getAction();
|
||||
|
@ -113,7 +115,7 @@ public class AlarmService extends IntentService {
|
|||
|
||||
if( ACTION_APP_DATA_IMPORT_BEFORE.equals( action ) ){
|
||||
alarm_manager.cancel( pi_next );
|
||||
for( SavedAccount a : SavedAccount.loadAccountList( log ) ){
|
||||
for( SavedAccount a : SavedAccount.loadAccountList( log ) ){
|
||||
try{
|
||||
String notification_tag = Long.toString( a.db_id );
|
||||
notification_manager.cancel( notification_tag, NOTIFICATION_ID );
|
||||
|
@ -181,32 +183,33 @@ public class AlarmService extends IntentService {
|
|||
|
||||
final AtomicBoolean bAlarmRequired = new AtomicBoolean( false );
|
||||
final HashSet< String > muted_app = MutedApp.getNameSet();
|
||||
final HashSet< String > muted_word = MutedWord.getNameSet();
|
||||
final WordTrieTree muted_word = MutedWord.getNameSet();
|
||||
|
||||
LinkedList<Thread> thread_list = new LinkedList<>( );
|
||||
LinkedList< Thread > thread_list = new LinkedList<>();
|
||||
for( SavedAccount _a : account_list ){
|
||||
final SavedAccount account = _a;
|
||||
Thread t = new Thread( new Runnable() {
|
||||
@Override public void run(){
|
||||
|
||||
|
||||
try{
|
||||
if( account.notification_mention
|
||||
|| account.notification_boost
|
||||
|| account.notification_favourite
|
||||
|| account.notification_follow
|
||||
){
|
||||
bAlarmRequired.set(true);
|
||||
bAlarmRequired.set( true );
|
||||
|
||||
TootApiClient client = new TootApiClient( AlarmService.this, new TootApiClient.Callback() {
|
||||
@Override public boolean isApiCancelled(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void publishApiProgress( String s ){
|
||||
}
|
||||
} );
|
||||
|
||||
ArrayList< Data > data_list = new ArrayList<>();
|
||||
checkAccount( client, data_list, account, muted_app ,muted_word);
|
||||
checkAccount( client, data_list, account, muted_app, muted_word );
|
||||
showNotification( account.db_id, data_list );
|
||||
}
|
||||
}catch( Throwable ex ){
|
||||
|
@ -214,15 +217,15 @@ public class AlarmService extends IntentService {
|
|||
}
|
||||
}
|
||||
} );
|
||||
thread_list.add( t);
|
||||
thread_list.add( t );
|
||||
t.start();
|
||||
}
|
||||
|
||||
for( Thread t : thread_list ){
|
||||
try{
|
||||
t.join();
|
||||
}catch(Throwable ex){
|
||||
ex.printStackTrace( );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +251,13 @@ public class AlarmService extends IntentService {
|
|||
|
||||
private static final String PATH_NOTIFICATIONS = "/api/v1/notifications";
|
||||
|
||||
private void checkAccount( TootApiClient client, ArrayList< Data > data_list, SavedAccount account, HashSet< String > muted_app, HashSet< String > muted_word ){
|
||||
private void checkAccount(
|
||||
@NonNull TootApiClient client
|
||||
, @NonNull ArrayList< Data > data_list
|
||||
, @NonNull SavedAccount account
|
||||
, @NonNull HashSet< String > muted_app
|
||||
, @NonNull WordTrieTree muted_word
|
||||
){
|
||||
log.d( "checkAccount account_db_id=%s", account.db_id );
|
||||
|
||||
NotificationTracking nr = NotificationTracking.load( account.db_id );
|
||||
|
@ -261,7 +270,7 @@ public class AlarmService extends IntentService {
|
|||
JSONArray array = new JSONArray( nr.last_data );
|
||||
for( int i = array.length() - 1 ; i >= 0 ; -- i ){
|
||||
JSONObject src = array.optJSONObject( i );
|
||||
update_sub( src, nr, account, dst_array, data_list, duplicate_check, muted_app ,muted_word);
|
||||
update_sub( src, nr, account, dst_array, data_list, duplicate_check, muted_app, muted_word );
|
||||
}
|
||||
}catch( JSONException ex ){
|
||||
ex.printStackTrace();
|
||||
|
@ -318,17 +327,16 @@ public class AlarmService extends IntentService {
|
|||
}
|
||||
|
||||
void update_sub(
|
||||
JSONObject src
|
||||
, NotificationTracking nr
|
||||
, SavedAccount account
|
||||
, ArrayList< JSONObject > dst_array
|
||||
, ArrayList< Data > data_list
|
||||
, HashSet< Long > duplicate_check
|
||||
, HashSet< String > muted_app
|
||||
, HashSet< String > muted_word
|
||||
@NonNull JSONObject src
|
||||
, @NonNull NotificationTracking nr
|
||||
, @NonNull SavedAccount account
|
||||
, @NonNull ArrayList< JSONObject > dst_array
|
||||
, @NonNull ArrayList< Data > data_list
|
||||
, @NonNull HashSet< Long > duplicate_check
|
||||
, @NonNull HashSet< String > muted_app
|
||||
, @NonNull WordTrieTree muted_word
|
||||
)
|
||||
throws JSONException
|
||||
{
|
||||
throws JSONException{
|
||||
|
||||
long id = src.optLong( "id" );
|
||||
|
||||
|
@ -360,7 +368,7 @@ public class AlarmService extends IntentService {
|
|||
{
|
||||
TootStatus status = notification.status;
|
||||
if( status != null ){
|
||||
if( status.checkMuted( muted_app,muted_word )){
|
||||
if( status.checkMuted( muted_app, muted_word ) ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +384,7 @@ public class AlarmService extends IntentService {
|
|||
dst_array.add( src );
|
||||
}
|
||||
|
||||
public String getNotificationLine( String type, CharSequence display_name ){
|
||||
public String getNotificationLine( @NonNull String type, @NonNull CharSequence display_name ){
|
||||
if( TootNotification.TYPE_FAVOURITE.equals( type ) ){
|
||||
return "- " + getString( R.string.display_name_favourited_by, display_name );
|
||||
}
|
||||
|
@ -392,7 +400,7 @@ public class AlarmService extends IntentService {
|
|||
return "- " + "?";
|
||||
}
|
||||
|
||||
private void showNotification( long account_db_id, ArrayList< Data > data_list ){
|
||||
private void showNotification( long account_db_id, @NonNull ArrayList< Data > data_list ){
|
||||
String notification_tag = Long.toString( account_db_id );
|
||||
if( data_list.isEmpty() ){
|
||||
notification_manager.cancel( notification_tag, NOTIFICATION_ID );
|
||||
|
@ -484,7 +492,7 @@ public class AlarmService extends IntentService {
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// Activity との連携
|
||||
|
||||
public static void startCheck( Context context ){
|
||||
public static void startCheck( @NonNull Context context ){
|
||||
Intent intent = new Intent( context, AlarmReceiver.class );
|
||||
context.sendBroadcast( intent );
|
||||
}
|
||||
|
@ -496,7 +504,7 @@ public class AlarmService extends IntentService {
|
|||
|
||||
static final ConcurrentLinkedQueue< InjectData > inject_queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public static void injectData( Context context, long account_db_id, TootNotification.List src ){
|
||||
public static void injectData( @NonNull Context context, long account_db_id, @NonNull TootNotification.List src ){
|
||||
InjectData data = new InjectData();
|
||||
data.account_db_id = account_db_id;
|
||||
data.list.addAll( src );
|
||||
|
@ -590,7 +598,7 @@ public class AlarmService extends IntentService {
|
|||
}
|
||||
}
|
||||
|
||||
public static void dataRemoved( Context context, long db_id ){
|
||||
public static void dataRemoved( @NonNull Context context, long db_id ){
|
||||
Intent intent = new Intent( context, AlarmService.class );
|
||||
intent.putExtra( EXTRA_DB_ID, db_id );
|
||||
intent.setAction( ACTION_DATA_DELETED );
|
||||
|
|
|
@ -271,6 +271,7 @@ public class AppDataExporter {
|
|||
case Pref.KEY_DONT_REFRESH_ON_RESUME:
|
||||
case Pref.KEY_DONT_SCREEN_OFF:
|
||||
case Pref.KEY_DISABLE_TABLET_MODE:
|
||||
case Pref.KEY_DONT_CROP_MEDIA_THUMBNAIL:
|
||||
boolean bv = reader.nextBoolean();
|
||||
e.putBoolean( k, bv );
|
||||
break;
|
||||
|
|
|
@ -42,6 +42,7 @@ import jp.juggler.subwaytooter.table.UserRelation;
|
|||
import jp.juggler.subwaytooter.util.BucketList;
|
||||
import jp.juggler.subwaytooter.api.DuplicateMap;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree;
|
||||
import jp.juggler.subwaytooter.view.MyListView;
|
||||
import jp.juggler.subwaytooter.util.ScrollPosition;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
@ -794,7 +795,7 @@ class Column implements StreamReader.Callback {
|
|||
ArrayList< Object > tmp_list = new ArrayList<>( list_data.size() );
|
||||
|
||||
HashSet< String > muted_app = MutedApp.getNameSet();
|
||||
HashSet< String > muted_word = MutedWord.getNameSet();
|
||||
WordTrieTree muted_word = MutedWord.getNameSet();
|
||||
|
||||
for( Object o : list_data ){
|
||||
if( o instanceof TootStatus ){
|
||||
|
@ -825,7 +826,7 @@ class Column implements StreamReader.Callback {
|
|||
|
||||
private Pattern column_regex_filter;
|
||||
private HashSet< String > muted_app;
|
||||
private HashSet< String > muted_word;
|
||||
private WordTrieTree muted_word;
|
||||
|
||||
private void initFilter(){
|
||||
column_regex_filter = null;
|
||||
|
|
|
@ -405,10 +405,12 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
iv.setVisibility( View.GONE );
|
||||
}else{
|
||||
iv.setVisibility( View.VISIBLE );
|
||||
iv.setScaleType( activity.dont_crop_media_thumbnail ? ImageView.ScaleType.FIT_CENTER : ImageView.ScaleType.CENTER_CROP );
|
||||
|
||||
TootAttachment ta = status.media_attachments.get( idx );
|
||||
String url = ta.preview_url;
|
||||
if( TextUtils.isEmpty( url ) ) url = ta.remote_url;
|
||||
iv.setCornerRadius( activity.pref, 16f ); // 正方形じゃないせいか、うまく動かない activity.density * 4f );
|
||||
iv.setCornerRadius( activity.pref,0f ); // 正方形じゃないせいか、うまく動かない activity.density * 4f );
|
||||
iv.setImageUrl( access_info.supplyBaseUrl( url ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public class Pref {
|
|||
static final String KEY_COLUMN_WIDTH = "ColumnWidth";
|
||||
static final String KEY_MEDIA_THUMB_HEIGHT = "MediaThumbHeight";
|
||||
static final String KEY_TIMELINE_FONT = "timeline_font";
|
||||
static final String KEY_DONT_CROP_MEDIA_THUMBNAIL = "DontCropMediaThumb";
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ public class TootAccount {
|
|||
dst.followers_count = src.optLong( "followers_count" );
|
||||
dst.following_count = src.optLong( "following_count" );
|
||||
dst.statuses_count = src.optLong( "statuses_count" );
|
||||
dst.note = HTMLDecoder.decodeHTML( account,Utils.optStringX( src, "note" ) );
|
||||
dst.note = HTMLDecoder.decodeHTML( account,Utils.optStringX( src, "note" ) ,true,null);
|
||||
dst.url = Utils.optStringX( src, "url" );
|
||||
dst.avatar = Utils.optStringX( src, "avatar" ); // "https:\/\/mastodon.juggler.jp\/system\/accounts\/avatars\/000\/000\/148\/original\/0a468974fac5a448.PNG?1492081886",
|
||||
dst.avatar_static = Utils.optStringX( src, "avatar_static" ); // "https:\/\/mastodon.juggler.jp\/system\/accounts\/avatars\/000\/000\/148\/original\/0a468974fac5a448.PNG?1492081886",
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.text.TextUtils;
|
|||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
@ -19,11 +18,11 @@ import java.util.TimeZone;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
||||
import jp.juggler.subwaytooter.util.LinkClickContext;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree;
|
||||
|
||||
public class TootStatus extends TootId {
|
||||
|
||||
|
@ -143,12 +142,12 @@ public class TootStatus extends TootId {
|
|||
status.application = TootApplication.parse( log, src.optJSONObject( "application" ) ); // null
|
||||
|
||||
status.time_created_at = parseTime( log, status.created_at );
|
||||
status.decoded_content = HTMLDecoder.decodeHTML( account, status.content );
|
||||
status.decoded_content = HTMLDecoder.decodeHTML( account, status.content ,true,status.media_attachments );
|
||||
// status.decoded_tags = HTMLDecoder.decodeTags( account,status.tags );
|
||||
status.decoded_mentions = HTMLDecoder.decodeMentions( account, status.mentions );
|
||||
|
||||
if( ! TextUtils.isEmpty( status.spoiler_text ) ){
|
||||
status.decoded_spoiler_text = HTMLDecoder.decodeHTML( account, status.spoiler_text );
|
||||
status.decoded_spoiler_text = HTMLDecoder.decodeHTML( account, status.spoiler_text ,true,status.media_attachments);
|
||||
}
|
||||
return status;
|
||||
}catch( Throwable ex ){
|
||||
|
@ -233,16 +232,16 @@ public class TootStatus extends TootId {
|
|||
throw new IndexOutOfBoundsException( "visibility not in range" );
|
||||
}
|
||||
|
||||
public void updateNickname( SavedAccount access_info ){
|
||||
decoded_content = HTMLDecoder.decodeHTML( access_info, content );
|
||||
decoded_mentions = HTMLDecoder.decodeMentions( access_info, mentions );
|
||||
|
||||
if( ! TextUtils.isEmpty( spoiler_text ) ){
|
||||
decoded_spoiler_text = HTMLDecoder.decodeHTML( access_info, spoiler_text );
|
||||
}
|
||||
}
|
||||
// public void updateNickname( SavedAccount access_info ){
|
||||
// decoded_content = HTMLDecoder.decodeHTML( access_info, content );
|
||||
// decoded_mentions = HTMLDecoder.decodeMentions( access_info, mentions );
|
||||
//
|
||||
// if( ! TextUtils.isEmpty( spoiler_text ) ){
|
||||
// decoded_spoiler_text = HTMLDecoder.decodeHTML( access_info, spoiler_text );
|
||||
// }
|
||||
// }
|
||||
|
||||
public boolean checkMuted( HashSet< String > muted_app, HashSet< String > muted_word ){
|
||||
public boolean checkMuted( @NonNull HashSet< String > muted_app, @NonNull WordTrieTree muted_word ){
|
||||
|
||||
// app mute
|
||||
if( application != null ){
|
||||
|
@ -255,22 +254,16 @@ public class TootStatus extends TootId {
|
|||
}
|
||||
|
||||
// word mute
|
||||
for( String word : muted_word ){
|
||||
if( decoded_content != null && decoded_content.toString().contains( word ) ){
|
||||
return true;
|
||||
}
|
||||
if( decoded_spoiler_text != null && decoded_spoiler_text.toString().contains( word ) ){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// reblog
|
||||
//noinspection RedundantIfStatement
|
||||
if( reblog != null && reblog.checkMuted( muted_app, muted_word ) ){
|
||||
if( decoded_content != null && muted_word.containsWord( decoded_content.toString() ) ){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if( decoded_spoiler_text != null && muted_word.containsWord( decoded_spoiler_text.toString() ) ){
|
||||
return true;
|
||||
}
|
||||
|
||||
// reblog
|
||||
return reblog != null && reblog.checkMuted( muted_app, muted_word );
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,11 @@ package jp.juggler.subwaytooter.table;
|
|||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.AppDataExporter;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree;
|
||||
|
||||
public class MutedWord {
|
||||
|
||||
|
@ -67,8 +64,8 @@ public class MutedWord {
|
|||
}
|
||||
}
|
||||
|
||||
public static HashSet< String > getNameSet(){
|
||||
HashSet< String > dst = new HashSet<>();
|
||||
public static WordTrieTree getNameSet(){
|
||||
WordTrieTree dst = new WordTrieTree();
|
||||
try{
|
||||
Cursor cursor = App1.getDB().query( table, null, null, null, null, null, null );
|
||||
if( cursor != null ){
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package jp.juggler.subwaytooter.util;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jp.juggler.subwaytooter.api.entity.TootAttachment;
|
||||
import jp.juggler.subwaytooter.api.entity.TootMention;
|
||||
|
||||
public class HTMLDecoder {
|
||||
|
@ -127,31 +131,47 @@ public class HTMLDecoder {
|
|||
if( DEBUG_HTML_PARSER ) log.d( "parseChild: %s)%s", indent, tag );
|
||||
}
|
||||
|
||||
void encodeSpan( LinkClickContext account, SpannableStringBuilder sb ){
|
||||
void encodeSpan(
|
||||
LinkClickContext account
|
||||
, SpannableStringBuilder sb
|
||||
, boolean bShort
|
||||
, @Nullable TootAttachment.List list_attachment
|
||||
){
|
||||
if( TAG_TEXT.equals( tag ) ){
|
||||
sb.append( Emojione.decodeEmoji( decodeEntity( text ) ) );
|
||||
return;
|
||||
}
|
||||
if( DEBUG_HTML_PARSER ) sb.append( "(start " ).append( tag ).append( ")" );
|
||||
|
||||
int start = sb.length();
|
||||
SpannableStringBuilder sb_tmp;
|
||||
if( bShort && "a".equals( tag ) ){
|
||||
sb_tmp = new SpannableStringBuilder( );
|
||||
}else{
|
||||
sb_tmp = sb;
|
||||
}
|
||||
|
||||
int start = sb_tmp.length();
|
||||
|
||||
for( Node child : child_nodes ){
|
||||
child.encodeSpan( account, sb );
|
||||
child.encodeSpan( account, sb_tmp ,bShort,list_attachment );
|
||||
}
|
||||
|
||||
int end = sb.length();
|
||||
int end = sb_tmp.length();
|
||||
|
||||
if( bShort && "a".equals( tag ) ){
|
||||
start = sb.length();
|
||||
sb.append( encodeShortUrl( sb_tmp.toString(), getHref(), list_attachment ) );
|
||||
end = sb.length();
|
||||
}
|
||||
|
||||
if( end > start && "a".equals( tag ) ){
|
||||
Matcher m = reHref.matcher( text );
|
||||
if( m.find() ){
|
||||
final String href = decodeEntity( m.group( 1 ) );
|
||||
if( ! TextUtils.isEmpty( href ) ){
|
||||
MyClickableSpan span = new MyClickableSpan(account,href,account.findAcctColor( href ) ) ;
|
||||
sb.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
|
||||
}
|
||||
String href = getHref();
|
||||
if( href != null){
|
||||
MyClickableSpan span = new MyClickableSpan(account,href,account.findAcctColor( href ) ) ;
|
||||
sb.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
|
||||
}
|
||||
}
|
||||
|
||||
if( DEBUG_HTML_PARSER ) sb.append( "(end " ).append( tag ).append( ")" );
|
||||
|
||||
if( "br".equals( tag ) ) sb.append( '\n' );
|
||||
|
@ -163,35 +183,61 @@ public class HTMLDecoder {
|
|||
}
|
||||
}
|
||||
|
||||
void encodeSpanForClipboard( LinkClickContext account, SpannableStringBuilder sb ){
|
||||
encodeSpan( account, sb );
|
||||
// 現時点ではURLの一部を非表示にするとかはしてないので
|
||||
// そのままテキストにしたものをコピーして大丈夫なはず
|
||||
private String getHref(){
|
||||
Matcher m = reHref.matcher( text );
|
||||
if( m.find() ){
|
||||
final String href = decodeEntity( m.group( 1 ) );
|
||||
if( ! TextUtils.isEmpty( href ) ){
|
||||
return href;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static CharSequence decodeHTMLForClipboard( LinkClickContext account, String src ){
|
||||
try{
|
||||
TokenParser tracker = new TokenParser( src );
|
||||
Node rootNode = new Node();
|
||||
rootNode.parseChild( tracker, "" );
|
||||
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
|
||||
rootNode.encodeSpanForClipboard( account, sb );
|
||||
int end = sb.length();
|
||||
while( end > 0 && Character.isWhitespace( sb.charAt( end - 1 ) ) ) -- end;
|
||||
if( end < sb.length() ){
|
||||
sb.delete( end, sb.length() );
|
||||
|
||||
boolean is_media_attachment( @Nullable TootAttachment.List list_attachment,@Nullable String href){
|
||||
if( href == null || list_attachment == null ) return false;
|
||||
for( TootAttachment a : list_attachment ){
|
||||
if( href.equals( a.remote_url )
|
||||
|| href.equals( a.url )
|
||||
|| href.equals( a.text_url )
|
||||
) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private CharSequence encodeShortUrl(
|
||||
String display_url
|
||||
, @Nullable String href
|
||||
, @Nullable TootAttachment.List list_attachment
|
||||
){
|
||||
if( ! display_url.startsWith( "http" ) ){
|
||||
// ハッシュタグやメンションはいじらない
|
||||
return display_url;
|
||||
}
|
||||
|
||||
// sb.append( "\n" );
|
||||
// sb.append( src );
|
||||
return sb;
|
||||
if( is_media_attachment( list_attachment,href )){
|
||||
return Emojione.decodeEmoji( ":frame_photo:" );
|
||||
}
|
||||
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
try{
|
||||
Uri uri = Uri.parse( display_url );
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( uri.getAuthority() );
|
||||
String a = uri.getEncodedPath();
|
||||
String q = uri.getEncodedQuery();
|
||||
String f = uri.getEncodedFragment();
|
||||
String remain = a + ( q == null ? "" : "?" + q ) + ( f == null ? "" : "#" + f );
|
||||
if( remain.length() > 10 ){
|
||||
sb.append( remain.substring( 0, 10 ) );
|
||||
sb.append( "…" );
|
||||
}else{
|
||||
sb.append( remain );
|
||||
}
|
||||
return sb;
|
||||
}catch(Throwable ex){
|
||||
ex.printStackTrace();
|
||||
return display_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +245,12 @@ public class HTMLDecoder {
|
|||
return Character.isWhitespace( c ) || c == 0x0a || c == 0x0d;
|
||||
}
|
||||
|
||||
public static SpannableStringBuilder decodeHTML( LinkClickContext account, String src ){
|
||||
public static SpannableStringBuilder decodeHTML(
|
||||
LinkClickContext account
|
||||
, String src
|
||||
, boolean bShort
|
||||
, @Nullable TootAttachment.List list_attachment
|
||||
){
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
try{
|
||||
if( src != null ){
|
||||
|
@ -207,16 +258,15 @@ public class HTMLDecoder {
|
|||
Node rootNode = new Node();
|
||||
rootNode.parseChild( tracker, "" );
|
||||
|
||||
rootNode.encodeSpan( account, sb );
|
||||
rootNode.encodeSpan( account, sb ,bShort,list_attachment);
|
||||
int end = sb.length();
|
||||
while( end > 0 && isWhitespace( sb.charAt( end - 1 ) ) ) -- end;
|
||||
if( end < sb.length() ){
|
||||
sb.delete( end, sb.length() );
|
||||
}
|
||||
|
||||
// sb.append( "\n" );
|
||||
// sb.append( src );
|
||||
|
||||
// sb.append( "\n" );
|
||||
// sb.append(src);
|
||||
}
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package jp.juggler.subwaytooter.util;
|
||||
|
||||
import android.support.v4.util.SparseArrayCompat;
|
||||
|
||||
public class WordTrieTree {
|
||||
|
||||
static class Node{
|
||||
|
||||
final SparseArrayCompat<Node> child_nodes = new SparseArrayCompat<>( );
|
||||
|
||||
boolean is_end;
|
||||
|
||||
boolean match( String s, int offset, int length ){
|
||||
|
||||
if( is_end ) return true;
|
||||
|
||||
if( length <= 0 ){
|
||||
return false;
|
||||
}
|
||||
|
||||
int c = s.charAt( offset );
|
||||
++ offset;
|
||||
-- length;
|
||||
Node n = child_nodes.get( c );
|
||||
return n != null && n.match( s, offset, length );
|
||||
}
|
||||
|
||||
public void add( String s, int offset, int length ){
|
||||
// NGワード用なので、既に終端を含むならより詳細な情報は必要がない
|
||||
if( is_end ) return;
|
||||
|
||||
// このノードは終端を持つ
|
||||
if( length <= 0 ){
|
||||
is_end = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int c = s.charAt( offset );
|
||||
++ offset;
|
||||
-- length;
|
||||
Node n = child_nodes.get( c );
|
||||
if( n == null ){
|
||||
child_nodes.put( c, n = new Node() );
|
||||
}
|
||||
n.add( s, offset, length );
|
||||
}
|
||||
}
|
||||
|
||||
final Node node_root = new Node();
|
||||
|
||||
public void add( String s ){
|
||||
node_root.add( s ,0,s.length() );
|
||||
}
|
||||
|
||||
public boolean containsWord(String src){
|
||||
if( src==null ) return false;
|
||||
|
||||
for(int i=0,ie=src.length();i<ie;++i){
|
||||
if( node_root.match( src, i,ie-i) ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -112,6 +112,24 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/dont_crop_media_thumbnail"
|
||||
/>
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
|
||||
<Switch
|
||||
android:id="@+id/swDontCropMediaThumb"
|
||||
style="@style/setting_horizontal_stretch"
|
||||
android:gravity="center"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -329,9 +329,9 @@
|
|||
<string name="user_id_conversion_failed">User id conversion failed.</string>
|
||||
<string name="draft_deleted">Draft deleted.</string>
|
||||
<string name="draft_picker_desc">Long tap to delete.</string>
|
||||
<string name="dont_crop_media_thumbnail">Don\'t crop media thumbnail</string>
|
||||
|
||||
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->
|
||||
<!--<string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>-->
|
||||
<!--<string name="abc_action_bar_up_description">Revenir en haut de la page</string>-->
|
||||
|
|
|
@ -617,4 +617,6 @@
|
|||
<string name="user_id_conversion_failed">ユーザIDを変換できません</string>
|
||||
<string name="draft_deleted">下書きを削除しました</string>
|
||||
<string name="draft_picker_desc">長押しで削除</string>
|
||||
<string name="dont_crop_media_thumbnail">添付メディアのサムネイルをクロップしない(アプリ再起動が必要)</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -326,4 +326,5 @@
|
|||
<string name="user_id_conversion_failed">User id conversion failed.</string>
|
||||
<string name="draft_picker_desc">Long tap to delete.</string>
|
||||
<string name="draft_deleted">Draft deleted.</string>
|
||||
<string name="dont_crop_media_thumbnail">Don\'t crop media thumbnail</string>
|
||||
</resources>
|
||||
|
|
|
@ -2,6 +2,8 @@ package jp.juggler.subwaytooter;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
|
@ -14,4 +16,34 @@ public class ExampleUnitTest {
|
|||
public void addition_isCorrect() throws Exception{
|
||||
assertEquals( 4, 2 + 2 );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWordTrieTree() throws Exception{
|
||||
WordTrieTree wtt = new WordTrieTree();
|
||||
|
||||
assertEquals( false, wtt.containsWord( null) );
|
||||
assertEquals( false, wtt.containsWord( "") );
|
||||
assertEquals( false, wtt.containsWord( "1") );
|
||||
|
||||
wtt.add("abc");
|
||||
wtt.add("abd");
|
||||
wtt.add("def");
|
||||
assertEquals( false, wtt.containsWord( null) );
|
||||
assertEquals( false, wtt.containsWord( "") );
|
||||
assertEquals( false, wtt.containsWord( "1") );
|
||||
|
||||
assertEquals( false, wtt.containsWord( "a") );
|
||||
assertEquals( false, wtt.containsWord( "ab") );
|
||||
assertEquals( true, wtt.containsWord( "abc") );
|
||||
assertEquals( true, wtt.containsWord( " abc ") );
|
||||
assertEquals( true, wtt.containsWord( "abd") );
|
||||
assertEquals( true, wtt.containsWord( " abd ") );
|
||||
assertEquals( false, wtt.containsWord( "abe") );
|
||||
assertEquals( false, wtt.containsWord( " abe ") );
|
||||
|
||||
assertEquals( false, wtt.containsWord( "d") );
|
||||
assertEquals( false, wtt.containsWord( "de") );
|
||||
assertEquals( true, wtt.containsWord( "def") );
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue