アプリ設定で通知音などをON/OFF。簡略ビューでお気に入り/ブーストしたら結果をトースト表示。フォローリクエスト一覧とFRの許可/却下。ギャップを読んだ後のスクロール位置を下端に。

This commit is contained in:
tateisu 2017-04-30 21:02:58 +09:00
parent 74d147e8e8
commit 65fe123f92
13 changed files with 382 additions and 159 deletions

View File

@ -9,8 +9,8 @@ android {
applicationId "jp.juggler.subwaytooter"
minSdkVersion 21
targetSdkVersion 25
versionCode 24
versionName "0.2.4"
versionCode 25
versionName "0.2.5"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

View File

@ -9,6 +9,7 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Spinner;
import android.widget.Switch;
@ -24,7 +25,7 @@ public class ActAppSetting extends AppCompatActivity implements CompoundButton.O
@Override
protected void onCreate( @Nullable Bundle savedInstanceState ){
super.onCreate( savedInstanceState );
App1.setActivityTheme(this,false);
App1.setActivityTheme( this, false );
initUI();
pref = Pref.pref( this );
@ -40,12 +41,14 @@ public class ActAppSetting extends AppCompatActivity implements CompoundButton.O
Spinner spBackButtonAction;
Spinner spUITheme;
static final int BACK_ASK_ALWAYS =0;
static final int BACK_CLOSE_COLUMN =1;
static final int BACK_OPEN_COLUMN_LIST =2;
static final int BACK_EXIT_APP =3;
CheckBox cbNotificationSound;
CheckBox cbNotificationVibration;
CheckBox cbNotificationLED;
static final int BACK_ASK_ALWAYS = 0;
static final int BACK_CLOSE_COLUMN = 1;
static final int BACK_OPEN_COLUMN_LIST = 2;
static final int BACK_EXIT_APP = 3;
private void initUI(){
setContentView( R.layout.act_app_setting );
@ -61,6 +64,13 @@ public class ActAppSetting extends AppCompatActivity implements CompoundButton.O
swSimpleList = (Switch) findViewById( R.id.swSimpleList );
swSimpleList.setOnCheckedChangeListener( this );
cbNotificationSound = (CheckBox) findViewById( R.id.cbNotificationSound );
cbNotificationVibration = (CheckBox) findViewById( R.id.cbNotificationVibration );
cbNotificationLED = (CheckBox) findViewById( R.id.cbNotificationLED );
cbNotificationSound.setOnCheckedChangeListener( this );
cbNotificationVibration.setOnCheckedChangeListener( this );
cbNotificationLED.setOnCheckedChangeListener( this );
{
String[] caption_list = new String[ 4 ];
caption_list[ 0 ] = getString( R.string.ask_always );
@ -69,45 +79,54 @@ public class ActAppSetting extends AppCompatActivity implements CompoundButton.O
caption_list[ 3 ] = getString( R.string.app_exit );
ArrayAdapter< String > adapter = new ArrayAdapter<>( this, android.R.layout.simple_spinner_item, caption_list );
adapter.setDropDownViewResource( R.layout.lv_spinner_dropdown );
spBackButtonAction = (Spinner) findViewById( R.id.spBackButtonAction );
spBackButtonAction = (Spinner) findViewById( R.id.spBackButtonAction );
spBackButtonAction.setAdapter( adapter );
spBackButtonAction.setOnItemSelectedListener( this );
}
{
String[] caption_list = new String[ 2 ];
caption_list[ 0 ] = getString( R.string.theme_light );
caption_list[ 1 ] = getString( R.string.theme_dark );
ArrayAdapter< String > adapter = new ArrayAdapter<>( this, android.R.layout.simple_spinner_item, caption_list );
adapter.setDropDownViewResource( R.layout.lv_spinner_dropdown );
spUITheme = (Spinner) findViewById( R.id.spUITheme );
spUITheme = (Spinner) findViewById( R.id.spUITheme );
spUITheme.setAdapter( adapter );
spUITheme.setOnItemSelectedListener( this );
}
}
boolean load_busy;
private void loadUIFromData(){
load_busy =true;
load_busy = true;
swDontConfirmBeforeCloseColumn.setChecked( pref.getBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, false ) );
swPriorLocalURL.setChecked( pref.getBoolean( Pref.KEY_PRIOR_LOCAL_URL, false ) );
swDisableFastScroller.setChecked( pref.getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, false ) );
swSimpleList.setChecked( pref.getBoolean(Pref.KEY_SIMPLE_LIST,false) );
spBackButtonAction.setSelection( pref.getInt(Pref.KEY_BACK_BUTTON_ACTION,0) );
spUITheme.setSelection( pref.getInt(Pref.KEY_UI_THEME,0) );
swSimpleList.setChecked( pref.getBoolean( Pref.KEY_SIMPLE_LIST, false ) );
cbNotificationSound.setChecked( pref.getBoolean( Pref.KEY_NOTIFICATION_SOUND, true ) );
cbNotificationVibration.setChecked( pref.getBoolean( Pref.KEY_NOTIFICATION_VIBRATION, true ) );
cbNotificationLED.setChecked( pref.getBoolean( Pref.KEY_NOTIFICATION_LED, true ) );
spBackButtonAction.setSelection( pref.getInt( Pref.KEY_BACK_BUTTON_ACTION, 0 ) );
spUITheme.setSelection( pref.getInt( Pref.KEY_UI_THEME, 0 ) );
load_busy = false;
}
private void saveUIToData(){
if(load_busy) return;
if( load_busy ) return;
pref.edit()
.putBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, swDontConfirmBeforeCloseColumn.isChecked() )
.putBoolean( Pref.KEY_PRIOR_LOCAL_URL, swPriorLocalURL.isChecked() )
.putBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, swDisableFastScroller.isChecked() )
.putBoolean( Pref.KEY_SIMPLE_LIST, swSimpleList.isChecked() )
.putBoolean( Pref.KEY_NOTIFICATION_SOUND, cbNotificationSound.isChecked() )
.putBoolean( Pref.KEY_NOTIFICATION_VIBRATION, cbNotificationVibration.isChecked() )
.putBoolean( Pref.KEY_NOTIFICATION_LED, cbNotificationLED.isChecked() )
.putInt( Pref.KEY_BACK_BUTTON_ACTION, spBackButtonAction.getSelectedItemPosition() )
.putInt( Pref.KEY_UI_THEME, spUITheme.getSelectedItemPosition() )
.apply();
@ -117,7 +136,8 @@ public class ActAppSetting extends AppCompatActivity implements CompoundButton.O
saveUIToData();
}
@Override public void onItemSelected( AdapterView< ? > parent, View view, int position, long id ){
@Override
public void onItemSelected( AdapterView< ? > parent, View view, int position, long id ){
saveUIToData();
}

View File

@ -318,6 +318,9 @@ public class ActMain extends AppCompatActivity
}else if( id == R.id.nav_add_blocks ){
performAddTimeline( Column.TYPE_BLOCKS );
}else if( id == R.id.nav_follow_requests ){
performAddTimeline( Column.TYPE_FOLLOW_REQUESTS );
}else if( id == R.id.nav_muted_app ){
startActivity( new Intent( this, ActMutedApp.class ) );
@ -888,6 +891,7 @@ public class ActMain extends AppCompatActivity
} );
}
//noinspection StatementWithEmptyBody
if( account_list.isEmpty() ){
// TODO ログインなしアカウントで開く選択肢
}
@ -1388,7 +1392,7 @@ public class ActMain extends AppCompatActivity
// cancelled.
}else if( relation != null ){
// ローカル操作成功もしくはリモートフォロー成功
showColumnMatchAccount( access_info );
if( callback != null ) callback.onRelationChanged();
@ -1458,9 +1462,9 @@ public class ActMain extends AppCompatActivity
if( result == null ){
// cancelled.
}else if( relation != null ){
showColumnMatchAccount( access_info );
if( callback != null ) callback.onRelationChanged();
}else if( locked && result.response.code() == 422 ){
@ -1623,6 +1627,57 @@ public class ActMain extends AppCompatActivity
}.execute();
}
private void callFollowRequestAuthorize( final SavedAccount access_info
, final TootAccount who, final boolean bAllow
){
new AsyncTask< Void, Void, TootApiResult >() {
@Override protected TootApiResult doInBackground( Void... params ){
TootApiClient client = new TootApiClient( ActMain.this, new TootApiClient.Callback() {
@Override public boolean isApiCancelled(){
return isCancelled();
}
@Override public void publishApiProgress( String s ){
}
} );
client.setAccount( access_info );
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, "" // 空データ
) );
return client.request(
"/api/v1/follow_requests/" + who.id+ ( bAllow ? "/authorize" : "/reject" )
, request_builder );
}
@Override
protected void onCancelled( TootApiResult result ){
onPostExecute( null );
}
@Override
protected void onPostExecute( TootApiResult result ){
//noinspection StatementWithEmptyBody
if( result == null ){
// cancelled.
}else if( result.object != null ){
for( Column column : pager_adapter.column_list ){
column.removeFollowRequest( access_info, who.id );
}
Utils.showToast( ActMain.this, false,( bAllow ? R.string.follow_request_authorized : R.string.follow_request_rejected),who.display_name );
}else{
Utils.showToast( ActMain.this, false, result.error );
}
}
}.execute();
}
private void deleteStatus( final SavedAccount access_info, final long status_id ){
new AsyncTask< Void, Void, TootApiResult >() {
@ -1690,13 +1745,12 @@ public class ActMain extends AppCompatActivity
@Override public void publishApiProgress( String s ){
}
} );
client.setAccount( account );
String sb = "account_id=" + Long.toString( status.account.id )
+ "&comment=" + Uri.encode( comment )
+ "&status_ids[]=" + Long.toString( status.id )
;
+ "&status_ids[]=" + Long.toString( status.id );
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
@ -1710,6 +1764,7 @@ public class ActMain extends AppCompatActivity
@Override protected void onCancelled( TootApiResult result ){
super.onPostExecute( result );
}
@Override protected void onPostExecute( TootApiResult result ){
//noinspection StatementWithEmptyBody
if( result == null ){
@ -1717,7 +1772,7 @@ public class ActMain extends AppCompatActivity
}else if( result.object != null ){
callback.onReportComplete( result );
}else{
Utils.showToast( ActMain.this,true,result.error );
Utils.showToast( ActMain.this, true, result.error );
}
}
@ -1726,13 +1781,13 @@ public class ActMain extends AppCompatActivity
private void openReportForm( final SavedAccount account, final TootAccount who, final TootStatus status ){
ReportForm.showReportForm( this, who, status, new ReportForm.ReportFormCallback() {
@Override public void startReport( final Dialog dialog, String comment ){
// レポートの送信を開始する
callReport( account, status, comment, new ReportCompleteCallback() {
@Override public void onReportComplete( TootApiResult result ){
// 成功したらダイアログを閉じる
dialog.dismiss();
Utils.showToast( ActMain.this, false, R.string.report_completed );
@ -1920,9 +1975,23 @@ public class ActMain extends AppCompatActivity
dialog.show( this, null );
}
public void openAccountMoreMenu( final SavedAccount access_info, final TootAccount who ){
public void openAccountMoreMenu( final SavedAccount access_info, final TootAccount who, int column_type ){
ActionsDialog dialog = new ActionsDialog();
if( column_type == Column.TYPE_FOLLOW_REQUESTS ){
dialog.addAction( getString( R.string.follow_request_ok ), new Runnable() {
@Override public void run(){
callFollowRequestAuthorize( access_info, who ,true);
}
} );
dialog.addAction( getString( R.string.follow_request_ng ), new Runnable() {
@Override public void run(){
callFollowRequestAuthorize( access_info, who ,false);
}
} );
}
dialog.addAction( getString( R.string.mention ), new Runnable() {
@Override public void run(){
performMention( access_info, who );
@ -1973,6 +2042,7 @@ public class ActMain extends AppCompatActivity
dialog.show( this, null );
}
private void openOSSLicense(){
startActivity( new Intent( this, ActOSSLicense.class ) );
}

View File

@ -6,6 +6,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.PowerManager;
import android.os.SystemClock;
@ -62,10 +63,11 @@ public class AlarmService extends IntentService {
NotificationManager notification_manager;
PowerManager.WakeLock wake_lock;
PendingIntent pi_next;
SharedPreferences pref;
@Override public void onCreate(){
super.onCreate();
log.d("ctor");
log.d( "ctor" );
alarm_manager = (AlarmManager) getApplicationContext().getSystemService( ALARM_SERVICE );
power_manager = (PowerManager) getApplicationContext().getSystemService( POWER_SERVICE );
@ -75,6 +77,8 @@ public class AlarmService extends IntentService {
wake_lock.setReferenceCounted( false );
wake_lock.acquire();
pref = Pref.pref( this );
// 次回レシーバーを起こすためのPendingIntent
Intent next_intent = new Intent( this, AlarmReceiver.class );
pi_next = PendingIntent.getBroadcast( this, PENDING_CODE_ALARM, next_intent, PendingIntent.FLAG_UPDATE_CURRENT );
@ -82,7 +86,7 @@ public class AlarmService extends IntentService {
}
@Override public void onDestroy(){
log.d("dtor");
log.d( "dtor" );
wake_lock.release();
super.onDestroy();
@ -96,10 +100,10 @@ public class AlarmService extends IntentService {
if( intent != null ){
String action = intent.getAction();
log.d("onHandleIntent action=%s",action);
log.d( "onHandleIntent action=%s", action );
if( ACTION_DATA_DELETED.equals( action ) ){
deleteCacheData(intent.getLongExtra( EXTRA_DB_ID ,-1L));
deleteCacheData( intent.getLongExtra( EXTRA_DB_ID, - 1L ) );
}else if( ACTION_DATA_INJECTED.equals( action ) ){
processInjectedData();
}else if( AlarmReceiver.ACTION_FROM_RECEIVER.equals( action ) ){
@ -109,23 +113,23 @@ public class AlarmService extends IntentService {
if( received_intent != null ){
action = received_intent.getAction();
log.d("received_intent.action=%s",action);
log.d( "received_intent.action=%s", action );
if( Intent.ACTION_BOOT_COMPLETED.equals( action ) ){
NotificationTracking.resetPostAll();
}else if( ACTION_NOTIFICATION_DELETE.equals( action ) ){
log.d( "Notification deleted!" );
long db_id = received_intent.getLongExtra( EXTRA_DB_ID ,0L);
long db_id = received_intent.getLongExtra( EXTRA_DB_ID, 0L );
NotificationTracking.updateRead( db_id );
return;
}else if( ACTION_NOTIFICATION_CLICK.equals( action ) ){
log.d( "Notification clicked!" );
long db_id = received_intent.getLongExtra( EXTRA_DB_ID ,0L);
long db_id = received_intent.getLongExtra( EXTRA_DB_ID, 0L );
NotificationTracking.updateRead( db_id );
notification_manager.cancel( Long.toString(db_id),NOTIFICATION_ID );
notification_manager.cancel( Long.toString( db_id ), NOTIFICATION_ID );
//
intent = new Intent( this, ActOAuthCallback.class );
intent.setData( Uri.parse( "subwaytooter://notification_click?db_id="+ db_id ) );
intent.setData( Uri.parse( "subwaytooter://notification_click?db_id=" + db_id ) );
intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
startActivity( intent );
return;
@ -134,7 +138,7 @@ public class AlarmService extends IntentService {
}
}
}
TootApiClient client = new TootApiClient( this, new TootApiClient.Callback() {
@Override public boolean isApiCancelled(){
return false;
@ -147,7 +151,7 @@ public class AlarmService extends IntentService {
boolean bAlarmRequired = false;
HashSet<String> muted_app = MutedApp.getNameSet();
HashSet< String > muted_app = MutedApp.getNameSet();
for( SavedAccount account : account_list ){
try{
@ -160,10 +164,10 @@ public class AlarmService extends IntentService {
ArrayList< Data > data_list = new ArrayList<>();
checkAccount( client, data_list, account ,muted_app);
checkAccount( client, data_list, account, muted_app );
showNotification( account.db_id, data_list );
}
}catch( Throwable ex ){
ex.printStackTrace();
@ -179,13 +183,12 @@ public class AlarmService extends IntentService {
, 60000L * 10
, pi_next
);
log.d("alarm set!");
log.d( "alarm set!" );
}else{
log.d("alarm is no longer required.");
log.d( "alarm is no longer required." );
}
}
private static class Data {
SavedAccount access_info;
TootNotification notification;
@ -193,11 +196,11 @@ 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 ){
log.d("checkAccount account_db_id=%s",account.db_id);
private void checkAccount( TootApiClient client, ArrayList< Data > data_list, SavedAccount account, HashSet< String > muted_app ){
log.d( "checkAccount account_db_id=%s", account.db_id );
NotificationTracking nr = NotificationTracking.load( account.db_id );
// まずキャッシュされたデータを処理する
HashSet< Long > duplicate_check = new HashSet<>();
ArrayList< JSONObject > dst_array = new ArrayList<>();
@ -206,7 +209,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);
update_sub( src, nr, account, dst_array, data_list, duplicate_check, muted_app );
}
}catch( JSONException ex ){
ex.printStackTrace();
@ -230,7 +233,7 @@ public class AlarmService extends IntentService {
JSONArray array = result.array;
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);
update_sub( src, nr, account, dst_array, data_list, duplicate_check, muted_app );
}
}catch( JSONException ex ){
ex.printStackTrace();
@ -269,7 +272,7 @@ public class AlarmService extends IntentService {
, ArrayList< JSONObject > dst_array
, ArrayList< Data > data_list
, HashSet< Long > duplicate_check
, HashSet<String> muted_app
, HashSet< String > muted_app
) throws JSONException{
long id = src.optLong( "id" );
@ -327,24 +330,24 @@ public class AlarmService extends IntentService {
public String getNotificationLine( String type, CharSequence display_name ){
if( TootNotification.TYPE_FAVOURITE.equals( type ) ){
return "- "+getString( R.string.display_name_favourited_by, display_name );
return "- " + getString( R.string.display_name_favourited_by, display_name );
}
if( TootNotification.TYPE_REBLOG.equals( type ) ){
return "- "+getString( R.string.display_name_boosted_by, display_name );
return "- " + getString( R.string.display_name_boosted_by, display_name );
}
if( TootNotification.TYPE_MENTION.equals( type ) ){
return "- "+getString( R.string.display_name_replied_by, display_name );
return "- " + getString( R.string.display_name_replied_by, display_name );
}
if( TootNotification.TYPE_FOLLOW.equals( type ) ){
return "- "+getString( R.string.display_name_followed_by, display_name );
return "- " + getString( R.string.display_name_followed_by, display_name );
}
return "- "+"?";
return "- " + "?";
}
private void showNotification( long account_db_id,ArrayList< Data > data_list ){
private void showNotification( long account_db_id, ArrayList< Data > data_list ){
String notification_tag = Long.toString( account_db_id );
if( data_list.isEmpty() ){
notification_manager.cancel( notification_tag,NOTIFICATION_ID );
notification_manager.cancel( notification_tag, NOTIFICATION_ID );
return;
}
@ -368,33 +371,45 @@ public class AlarmService extends IntentService {
// このマーカーは端末再起動時にリセットされるので再起動後は通知が出るはず
return;
}
nt.updatePost( item.notification.id, item.notification.time_created_at );
nt.updatePost( item.notification.id, item.notification.time_created_at );
// 通知タップ
Intent intent_click = new Intent( this, AlarmReceiver.class );
intent_click.putExtra(EXTRA_DB_ID,account_db_id);
intent_click.putExtra( EXTRA_DB_ID, account_db_id );
intent_click.setAction( ACTION_NOTIFICATION_CLICK );
Intent intent_delete = new Intent( this, AlarmReceiver.class );
intent_click.putExtra(EXTRA_DB_ID,account_db_id);
intent_click.putExtra( EXTRA_DB_ID, account_db_id );
intent_delete.setAction( ACTION_NOTIFICATION_DELETE );
PendingIntent pi_click = PendingIntent.getBroadcast( this, (256+(int)account_db_id), intent_click, PendingIntent.FLAG_UPDATE_CURRENT );
PendingIntent pi_click = PendingIntent.getBroadcast( this, ( 256 + (int) account_db_id ), intent_click, PendingIntent.FLAG_UPDATE_CURRENT );
// 通知を消去した時のPendingIntent
PendingIntent pi_delete = PendingIntent.getBroadcast( this, (Integer.MAX_VALUE-(int)account_db_id), intent_delete, PendingIntent.FLAG_UPDATE_CURRENT );
PendingIntent pi_delete = PendingIntent.getBroadcast( this, ( Integer.MAX_VALUE - (int) account_db_id ), intent_delete, PendingIntent.FLAG_UPDATE_CURRENT );
int iv = 0;
if( pref.getBoolean( Pref.KEY_NOTIFICATION_SOUND, true ) ){
iv |= NotificationCompat.DEFAULT_SOUND;
}
if( pref.getBoolean( Pref.KEY_NOTIFICATION_VIBRATION, true ) ){
iv |= NotificationCompat.DEFAULT_VIBRATE;
}
if( pref.getBoolean( Pref.KEY_NOTIFICATION_LED, true ) ){
iv |= NotificationCompat.DEFAULT_LIGHTS;
}
NotificationCompat.Builder builder = new NotificationCompat.Builder( this )
.setContentIntent( pi_click )
.setDeleteIntent( pi_delete )
.setAutoCancel( false )
.setSmallIcon( R.drawable.ic_notification ) // ここは常に白テーマのアイコンを使う
.setColor( ContextCompat.getColor(this, R.color.Light_colorAccent ) ) // ここは常に白テーマの色を使う
.setDefaults( NotificationCompat.DEFAULT_ALL )
.setColor( ContextCompat.getColor( this, R.color.Light_colorAccent ) ) // ここは常に白テーマの色を使う
.setDefaults( iv )
.setWhen( item.notification.time_created_at );
String a = getNotificationLine( item.notification.type, item.notification.account.display_name );
String acct = item.access_info.acct +" "+getString( R.string.app_name );
String acct = item.access_info.acct + " " + getString( R.string.app_name );
if( data_list.size() == 1 ){
builder.setContentTitle( a );
builder.setContentText( acct );
@ -415,14 +430,14 @@ public class AlarmService extends IntentService {
builder.setStyle( style );
}
notification_manager.notify( notification_tag,NOTIFICATION_ID, builder.build() );
notification_manager.notify( notification_tag, NOTIFICATION_ID, builder.build() );
}
////////////////////////////////////////////////////////////////////////////
// Activity との連携
public static void startCheck(Context context){
Intent intent = new Intent(context,AlarmReceiver.class);
public static void startCheck( Context context ){
Intent intent = new Intent( context, AlarmReceiver.class );
context.sendBroadcast( intent );
}
@ -466,7 +481,7 @@ public class AlarmService extends IntentService {
long id = src.optLong( "id" );
dst_array.add( src );
duplicate_check.add( id );
log.d("add old. id=%s",id);
log.d( "add old. id=%s", id );
}
}catch( JSONException ex ){
ex.printStackTrace();
@ -475,7 +490,7 @@ public class AlarmService extends IntentService {
for( TootNotification item : data.list ){
try{
if( duplicate_check.contains( item.id ) ){
log.d("skip duplicate. id=%s",item.id);
log.d( "skip duplicate. id=%s", item.id );
continue;
}
duplicate_check.add( item.id );
@ -487,7 +502,7 @@ public class AlarmService extends IntentService {
|| ( ! account.notification_favourite && TootNotification.TYPE_FAVOURITE.equals( type ) )
|| ( ! account.notification_follow && TootNotification.TYPE_FOLLOW.equals( type ) )
){
log.d("skip by setting. id=%s",item.id);
log.d( "skip by setting. id=%s", item.id );
continue;
}
@ -516,7 +531,7 @@ public class AlarmService extends IntentService {
JSONArray d = new JSONArray();
for( int i = 0 ; i < 10 ; ++ i ){
if( i >= dst_array.size() ){
log.d("inject %s data",i);
log.d( "inject %s data", i );
break;
}
d.put( dst_array.get( i ) );
@ -529,19 +544,20 @@ public class AlarmService extends IntentService {
public static void dataRemoved( Context context, long db_id ){
Intent intent = new Intent( context, AlarmService.class );
intent.putExtra( EXTRA_DB_ID,db_id );
intent.putExtra( EXTRA_DB_ID, db_id );
intent.setAction( ACTION_DATA_DELETED );
context.startService( intent );
}
private void deleteCacheData( long db_id ){
SavedAccount account = SavedAccount.loadAccount( log,db_id );
SavedAccount account = SavedAccount.loadAccount( log, db_id );
if( account == null ) return;
NotificationTracking nr = NotificationTracking.load( db_id );
nr.last_data = new JSONArray().toString();
nr.save();
}

View File

@ -65,6 +65,7 @@ class Column {
private static final String PATH_ACCOUNT_FOLLOWERS = "/api/v1/accounts/%d/followers?limit=" + READ_LIMIT; // 1:account_id
private static final String PATH_MUTES = "/api/v1/mutes?limit=" + READ_LIMIT; // 1:account_id
private static final String PATH_BLOCKS = "/api/v1/blocks?limit=" + READ_LIMIT; // 1:account_id
private static final String PATH_FOLLOW_REQUESTS = "/api/v1/follow_requests?limit=" + READ_LIMIT; // 1:account_id
// 他のリストを返すAPI
private static final String PATH_REPORTS = "/api/v1/reports?limit=" + READ_LIMIT;
@ -107,6 +108,7 @@ class Column {
static final int TYPE_SEARCH = 10;
static final int TYPE_MUTES = 11;
static final int TYPE_BLOCKS = 12;
static final int TYPE_FOLLOW_REQUESTS = 13;
@NonNull private final ActMain activity;
@NonNull final SavedAccount access_info;
@ -330,8 +332,12 @@ class Column {
}else{
return activity.getString( R.string.search );
}
case TYPE_FOLLOW_REQUESTS:
return activity.getString( R.string.follow_requests );
}
}
interface StatusEntryCallback {
void onIterate( TootStatus status );
@ -440,6 +446,29 @@ class Column {
}
}
public void removeFollowRequest( SavedAccount target_account, long who_id ){
if( ! target_account.acct.equals( access_info.acct ) ) return;
if( type == TYPE_FOLLOW_REQUESTS ){
ArrayList< Object > tmp_list = new ArrayList<>( list_data.size() );
for( Object o : list_data ){
if( o instanceof TootAccount ){
TootAccount item = (TootAccount) o;
if( item.id == who_id ) continue;
}
tmp_list.add( o );
}
if( tmp_list.size() != list_data.size() ){
list_data.clear();
list_data.addAll( tmp_list );
fireVisualCallback();
}
}else{
// 他のカラムでもフォロー状態の表示更新が必要
fireVisualCallback();
}
}
// 自分のステータスを削除した時に呼ばれる
void removeStatus( SavedAccount target_account, long status_id ){
@ -841,6 +870,9 @@ class Column {
case TYPE_BLOCKS:
return parseAccountList( client, PATH_BLOCKS );
case TYPE_FOLLOW_REQUESTS:
return parseAccountList( client, PATH_FOLLOW_REQUESTS );
case TYPE_FAVOURITES:
return getStatuses( client, PATH_FAVOURITES );
@ -1371,6 +1403,9 @@ class Column {
case TYPE_BLOCKS:
return getAccountList( client, PATH_BLOCKS );
case TYPE_FOLLOW_REQUESTS:
return getAccountList( client, PATH_FOLLOW_REQUESTS );
case TYPE_HASHTAG:
return getStatusList( client,
@ -1720,6 +1755,9 @@ class Column {
case TYPE_BLOCKS:
return getAccountList( client, PATH_BLOCKS );
case TYPE_FOLLOW_REQUESTS:
return getAccountList( client, PATH_FOLLOW_REQUESTS );
case TYPE_PROFILE:
switch( profile_tab ){
@ -1799,10 +1837,17 @@ class Column {
list_new.add( o );
}
int pos = list_data.indexOf( gap );
if( pos != - 1 ){
list_data.remove( pos );
list_data.addAll( pos, list_new );
// リフレッシュ開始時はリストの先頭を見ていたのだからスクロール範囲を調整したい
scroll_hack = pos + list_new.size() -2;
if( scroll_hack < 1 ) scroll_hack = 1;
}
}
}
@ -1844,14 +1889,12 @@ class Column {
who_set.add( a.id );
}else if( o instanceof TootStatus ){
s = (TootStatus) o;
a = s.account;
if( a != null ) who_set.add( a.id );
s = s.reblog;
if( s != null ){
a = s.account;
if( a != null ) who_set.add( a.id );
s = s.reblog;
if( s != null ){
a = s.account;
if( a != null ) who_set.add( a.id );
}
}
}else if( o instanceof TootNotification ){
n = (TootNotification) o;

View File

@ -1,5 +1,6 @@
package jp.juggler.subwaytooter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
@ -74,7 +75,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
return is_destroyed.get() || activity.isFinishing();
}
void onPageDestroy( View root ){
void onPageDestroy( @SuppressWarnings("UnusedParameters") View root ){
saveScrollPosition();
log.d( "onPageDestroy:%s", column.getColumnName( true ) );
column.removeVisualListener( this );
@ -95,11 +96,9 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
private TextView tvRegexFilterError;
private boolean bSimpleList;
void onPageCreate( View root, int page_idx, int page_count ){
log.d( "onPageCreate:%s", column.getColumnName( true ) );
( (TextView) root.findViewById( R.id.tvColumnIndex ) )
.setText( activity.getString( R.string.column_index, page_idx + 1, page_count ) );
@ -136,6 +135,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
case Column.TYPE_REPORTS:
case Column.TYPE_BLOCKS:
case Column.TYPE_MUTES:
case Column.TYPE_FOLLOW_REQUESTS:
case Column.TYPE_NOTIFICATIONS:
bAllowFilter = false;
break;
@ -246,11 +246,11 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
swipyRefreshLayout.setEnabled( false );
}
bSimpleList = activity.pref.getBoolean(Pref.KEY_SIMPLE_LIST,false);
bSimpleList = activity.pref.getBoolean( Pref.KEY_SIMPLE_LIST, false );
if( column.type == Column.TYPE_CONVERSATION ){
bSimpleList = false;
}
if(bSimpleList){
if( bSimpleList ){
listView.setOnItemClickListener( status_adapter );
}
@ -533,8 +533,8 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
btnFollow.setImageDrawable( null );
}else{
tvCreated.setText( TootStatus.formatTime( who.time_created_at ) );
ivBackground.setImageUrl( access_info.supplyBaseUrl(who.header_static), App1.getImageLoader() );
ivAvatar.setImageUrl( access_info.supplyBaseUrl(who.avatar_static), App1.getImageLoader() );
ivBackground.setImageUrl( access_info.supplyBaseUrl( who.header_static ), App1.getImageLoader() );
ivAvatar.setImageUrl( access_info.supplyBaseUrl( who.avatar_static ), App1.getImageLoader() );
tvDisplayName.setText( who.display_name );
String s = access_info.getFullAcct( who );
@ -548,8 +548,8 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
btnFollowing.setText( activity.getString( R.string.following ) + "\n" + who.following_count );
btnFollowers.setText( activity.getString( R.string.followers ) + "\n" + who.followers_count );
UserRelation relation = UserRelation.load( access_info.db_id,who.id );
Styler.setFollowIcon( activity,btnFollow,relation );
UserRelation relation = UserRelation.load( access_info.db_id, who.id );
Styler.setFollowIcon( activity, btnFollow, relation );
}
}
@ -580,14 +580,14 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
break;
case R.id.btnMore:
if( who != null){
activity.openAccountMoreMenu( access_info, who );
if( who != null ){
activity.openAccountMoreMenu( access_info, who ,column.type);
}
break;
case R.id.btnFollow:
if( who != null){
activity.openAccountMoreMenu( access_info, who );
if( who != null ){
activity.openAccountMoreMenu( access_info, who ,column.type);
}
break;
@ -639,8 +639,8 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
@Override
public void onItemClick( AdapterView< ? > parent, View view, int position, long id ){
Object tag = view.getTag();
if( tag instanceof StatusViewHolder){
((StatusViewHolder)tag).onItemClick(view);
if( tag instanceof StatusViewHolder ){
( (StatusViewHolder) tag ).onItemClick( view );
}
}
}
@ -684,7 +684,6 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
final ButtonsForStatus buttons_for_status;
final View llSearchTag;
final Button btnSearchTag;
@ -749,7 +748,6 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
ivMedia3.setOnClickListener( this );
ivMedia4.setOnClickListener( this );
btnFollow.setOnClickListener( this );
ivThumbnail.setOnClickListener( this );
// ここを個別タップにすると邪魔すぎる tvName.setOnClickListener( this );
@ -761,7 +759,6 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
tvMentions.setMovementMethod( MyLinkMovementMethod.getInstance() );
tvContentWarning.setMovementMethod( MyLinkMovementMethod.getInstance() );
}
void bind( Object item ){
@ -812,7 +809,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
//
showFollow( n.account );
}else if( TootNotification.TYPE_MENTION.equals( n.type ) ){
if(!bSimpleList){
if( ! bSimpleList ){
showBoost(
n.account
, n.time_created_at
@ -866,12 +863,12 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
private void showFollow( TootAccount who ){
account_follow = who;
llFollow.setVisibility( View.VISIBLE );
ivFollow.setImageUrl( access_info.supplyBaseUrl(who.avatar_static), App1.getImageLoader() );
ivFollow.setImageUrl( access_info.supplyBaseUrl( who.avatar_static ), App1.getImageLoader() );
tvFollowerName.setText( who.display_name );
tvFollowerAcct.setText( access_info.getFullAcct( who ) );
UserRelation relation = UserRelation.load( access_info.db_id,who.id);
Styler.setFollowIcon( activity,btnFollow,relation );
UserRelation relation = UserRelation.load( access_info.db_id, who.id );
Styler.setFollowIcon( activity, btnFollow, relation );
}
private void showStatus( ActMain activity, TootStatus status ){
@ -883,7 +880,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
tvTime.setText( TootStatus.formatTime( status.time_created_at ) );
tvName.setText( status.account.display_name );
ivThumbnail.setImageUrl( access_info.supplyBaseUrl(status.account.avatar_static), App1.getImageLoader() );
ivThumbnail.setImageUrl( access_info.supplyBaseUrl( status.account.avatar_static ), App1.getImageLoader() );
tvContent.setText( status.decoded_content );
// if( status.decoded_tags == null ){
@ -925,22 +922,22 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
btnShowMedia.setVisibility( ! is_shown ? View.VISIBLE : View.GONE );
}
if( buttons_for_status != null){
if( buttons_for_status != null ){
buttons_for_status.bind( status );
}
if( tvApplication != null ){
switch(column.type){
switch( column.type ){
default:
tvApplication.setVisibility( View.GONE );
break;
case Column.TYPE_CONVERSATION:
if( status.application == null ){
tvApplication.setVisibility( View.GONE );
}else{
tvApplication.setVisibility( View.VISIBLE );
tvApplication.setText( activity.getString(R.string.application_is,status.application.name));
tvApplication.setText( activity.getString( R.string.application_is, status.application.name ) );
}
break;
@ -948,8 +945,6 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
}
}
private void showContent( boolean shown ){
btnContentWarning.setText( shown ? R.string.hide : R.string.show );
llContents.setVisibility( shown ? View.VISIBLE : View.GONE );
@ -964,7 +959,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
TootAttachment ta = status.media_attachments.get( idx );
String url = ta.preview_url;
if( TextUtils.isEmpty( url ) ) url = ta.remote_url;
iv.setImageUrl( access_info.supplyBaseUrl(url), App1.getImageLoader() );
iv.setImageUrl( access_info.supplyBaseUrl( url ), App1.getImageLoader() );
}
}
@ -1008,7 +1003,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
activity.performOpenUser( access_info, account_follow );
break;
case R.id.btnFollow:
activity.openAccountMoreMenu( access_info, account_follow );
activity.openAccountMoreMenu( access_info, account_follow ,column.type);
break;
case R.id.btnSearchTag:
@ -1049,8 +1044,8 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
}
// 簡略ビューの時だけ呼ばれる
void onItemClick(View anchor){
void onItemClick( View anchor ){
if( status != null ){
// ポップアップを表示する
ListItemPopup popup = new ListItemPopup();
@ -1059,14 +1054,25 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
}
}
private final ActMain.RelationChangedCallback favourite_complete_callback = new ActMain.RelationChangedCallback() {
@Override public void onRelationChanged(){
Utils.showToast( activity, false, R.string.favourite_succeeded );
}
};
private final ActMain.RelationChangedCallback boost_complete_callback = new ActMain.RelationChangedCallback() {
@Override public void onRelationChanged(){
Utils.showToast( activity, false, R.string.boost_succeeded );
}
};
private class ButtonsForStatus implements View.OnClickListener {
final ImageButton btnConversation;
final ImageButton btnReply;
final Button btnBoost;
final Button btnFavourite;
final ImageButton btnMore;
ButtonsForStatus(View viewRoot){
ButtonsForStatus( View viewRoot ){
btnConversation = (ImageButton) viewRoot.findViewById( R.id.btnConversation );
btnReply = (ImageButton) viewRoot.findViewById( R.id.btnReply );
btnBoost = (Button) viewRoot.findViewById( R.id.btnBoost );
@ -1084,7 +1090,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
void bind( TootStatus status ){
this.status = status;
int color_normal = Styler.getAttributeColor( activity, R.attr.colorImageButton );
int color_accent = Styler.getAttributeColor( activity, R.attr.colorImageButtonAccent );
@ -1106,8 +1112,8 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
setButton( btnFavourite, true, color, R.attr.btn_favourite, Long.toString( status.favourites_count ) );
}
}
private void setButton( Button b, boolean enabled, int color, int icon_attr, String text ){
Drawable d = Styler.getAttributeDrawable( activity, icon_attr ).mutate();
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
@ -1121,7 +1127,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
@Override public void onClick( View v ){
if( close_window != null ) close_window.dismiss();
switch(v.getId()){
switch( v.getId() ){
case R.id.btnConversation:
activity.performConversation( access_info, status );
break;
@ -1129,74 +1135,74 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
activity.performReply( access_info, status );
break;
case R.id.btnBoost:
activity.performBoost( access_info, status, false ,null);
activity.performBoost( access_info, status, false, bSimpleList ? boost_complete_callback : null );
break;
case R.id.btnFavourite:
activity.performFavourite( access_info, status ,null);
activity.performFavourite( access_info, status, bSimpleList ? favourite_complete_callback : null );
break;
case R.id.btnMore:
activity.openStatusMoreMenu( access_info, status ,column.type);
activity.openStatusMoreMenu( access_info, status, column.type );
break;
}
}
}
private class ListItemPopup{
private class ListItemPopup {
final View viewRoot;
final ButtonsForStatus buttons_for_status;
ListItemPopup( ){
@SuppressLint("InflateParams") ListItemPopup(){
viewRoot = activity.getLayoutInflater().inflate( R.layout.list_item_popup, null, false );
buttons_for_status = new ButtonsForStatus( viewRoot );
}
PopupWindow window;
void show(View anchor, TootStatus status ){
void show( View anchor, TootStatus status ){
//
window = new PopupWindow(activity);
window = new PopupWindow( activity );
window.setWidth( WindowManager.LayoutParams.WRAP_CONTENT );
window.setHeight( WindowManager.LayoutParams.WRAP_CONTENT );
window.setContentView( viewRoot );
window.setBackgroundDrawable( new ColorDrawable(0x00000000) );
window.setTouchable(true);
window.setBackgroundDrawable( new ColorDrawable( 0x00000000 ) );
window.setTouchable( true );
window.setOutsideTouchable( true );
window.setTouchInterceptor( new View.OnTouchListener(){
window.setTouchInterceptor( new View.OnTouchListener() {
@Override public boolean onTouch( View v, MotionEvent event ){
if( MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_OUTSIDE){
if( MotionEventCompat.getActionMasked( event ) == MotionEvent.ACTION_OUTSIDE ){
window.dismiss();
listView.last_popup_close = SystemClock.elapsedRealtime();
listView.last_popup_close = SystemClock.elapsedRealtime();
return true;
}
return false;
}
} );
buttons_for_status.bind( status );
buttons_for_status.close_window = window;
int[] location = new int[2];
anchor.getLocationOnScreen(location);
int anchor_top = location[1];
int[] location = new int[ 2 ];
listView.getLocationOnScreen(location);
int listView_top = location[1];
anchor.getLocationOnScreen( location );
int anchor_top = location[ 1 ];
int clip_top = listView_top + (int)(0.5f + 8f * activity.density );
int clip_bottom = listView_top + listView.getHeight() - (int)(0.5f + 8f * activity.density );
listView.getLocationOnScreen( location );
int listView_top = location[ 1 ];
int popup_height = (int)(0.5f + (56f+24f) * activity.density );
int popup_y = anchor_top + anchor.getHeight()/2;
int clip_top = listView_top + (int) ( 0.5f + 8f * activity.density );
int clip_bottom = listView_top + listView.getHeight() - (int) ( 0.5f + 8f * activity.density );
if(popup_y < clip_top){
int popup_height = (int) ( 0.5f + ( 56f + 24f ) * activity.density );
int popup_y = anchor_top + anchor.getHeight() / 2;
if( popup_y < clip_top ){
// 画面外のは画面内にする
popup_y = clip_top;
}else if( clip_bottom - popup_y < popup_height ){
// 画面外のは画面内にする
if( popup_y > clip_bottom ) popup_y = clip_bottom;
// 画面の下側にあるならポップアップの吹き出しが下から出ているように見せる
viewRoot.findViewById( R.id.ivTriangleTop ).setVisibility( View.GONE );
viewRoot.findViewById( R.id.ivTriangleBottom ).setVisibility( View.VISIBLE );
@ -1204,7 +1210,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
}
window.showAtLocation(
listView
, Gravity.CENTER_HORIZONTAL|Gravity.TOP
, Gravity.CENTER_HORIZONTAL | Gravity.TOP
, 0
, popup_y
);

View File

@ -6,18 +6,20 @@ import android.preference.PreferenceManager;
class Pref {
static SharedPreferences pref( Context context ){
return PreferenceManager.getDefaultSharedPreferences( context );
}
private static final String KEY_BACK_TO_COLUMN_LIST ="BackToColumnList"; // 使わなくなった
private static final String KEY_BACK_TO_COLUMN_LIST = "BackToColumnList"; // 使わなくなった
static final String KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN ="DontConfirmBeforeCloseColumn";
static final String KEY_BACK_BUTTON_ACTION ="back_button_action";
static final String KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN = "DontConfirmBeforeCloseColumn";
static final String KEY_BACK_BUTTON_ACTION = "back_button_action";
static final String KEY_PRIOR_LOCAL_URL = "prior_local_url";
static final String KEY_DISABLE_FAST_SCROLLER = "disable_fast_scroller";
static final String KEY_UI_THEME = "ui_theme";
static final String KEY_SIMPLE_LIST = "simple_list";
static final String KEY_NOTIFICATION_SOUND = "notification_sound";
static final String KEY_NOTIFICATION_VIBRATION = "notification_vibration";
static final String KEY_NOTIFICATION_LED = "notification_led";
}

View File

@ -130,20 +130,20 @@ public class Utils {
// }
//
// 文字列とバイト列の変換
public static byte[] encodeUTF8( String str ){
@NonNull public static byte[] encodeUTF8( @NonNull String str ){
try{
return str.getBytes( "UTF-8" );
}catch( Throwable ex ){
return null; // 入力がnullの場合のみ発生
return new byte[0]; // 入力がnullの場合のみ発生
}
}
// 文字列とバイト列の変換
public static String decodeUTF8( byte[] data ){
@NonNull public static String decodeUTF8( @NonNull byte[] data ){
try{
return new String( data, "UTF-8" );
}catch( Throwable ex ){
return null; // 入力がnullの場合のみ発生
return ""; // 入力がnullの場合のみ発生
}
}

View File

@ -120,6 +120,40 @@ android:scrollbarStyle="outsideOverlay"
</LinearLayout>
<View style="@style/setting_divider"/>
<TextView
style="@style/setting_row_label"
android:text="@string/notification_option"
/>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationSound"
style="@style/setting_horizontal_stretch"
android:text="@string/sound"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationVibration"
style="@style/setting_horizontal_stretch"
android:text="@string/vibration"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationLED"
style="@style/setting_horizontal_stretch"
android:text="@string/led"
/>
</LinearLayout>
<View style="@style/setting_divider"/>
<!--<TextView-->

View File

@ -56,6 +56,11 @@
android:icon="?attr/btn_statuses"
android:title="@string/your_statuses"/>
<item
android:id="@+id/nav_follow_requests"
android:icon="?attr/ic_account_add"
android:title="@string/follow_requests"/>
<item
android:id="@+id/nav_add_mutes"
android:icon="?attr/ic_mute"

View File

@ -196,5 +196,14 @@
<string name="mute_app_desc">Swipe to delete. You may need reload to show status published by unmuted apps.</string>
<string name="mute_app_of">mute app \"%1$s\"</string>
<string name="muted_app">Muted apps</string>
<string name="follow_request_authorized">%1$s\'s follow request is authorized.</string>
<string name="follow_request_ng">Reject for follow request</string>
<string name="follow_request_ok">Authorize for follow request</string>
<string name="follow_request_rejected">%1$s\'s follow request is rejected.</string>
<string name="follow_requests">Follow requests</string>
<string name="led">LED</string>
<string name="notification_option">notification option</string>
<string name="sound">sound</string>
<string name="vibration">vibration</string>
</resources>

View File

@ -192,4 +192,13 @@
<string name="mute_app_desc">スワイプで解除。解除した後はカラムをリロードすると再表示されます</string>
<string name="mute_app_of">\"%1$s\" アプリをミュート</string>
<string name="muted_app">ミュートしたアプリ</string>
<string name="follow_request_authorized">%1$s からのフォローリクエストを許可しました</string>
<string name="follow_request_ng">フォローリクエストの却下</string>
<string name="follow_request_ok">フォローリクエストの許可</string>
<string name="follow_request_rejected">%1$s からのフォローリクエストを却下しました</string>
<string name="follow_requests">フォローリクエスト一覧</string>
<string name="led">LED</string>
<string name="notification_option">通知オプション</string>
<string name="sound"></string>
<string name="vibration">振動</string>
</resources>

View File

@ -193,4 +193,13 @@
<string name="app_was_muted">App was muted.</string>
<string name="favourite_succeeded">favourite succeeded</string>
<string name="boost_succeeded">boost succeeded</string>
<string name="notification_option">notification option</string>
<string name="sound">sound</string>
<string name="vibration">vibration</string>
<string name="led">LED</string>
<string name="follow_requests">Follow requests</string>
<string name="follow_request_ok">Authorize for follow request</string>
<string name="follow_request_ng">Reject for follow request</string>
<string name="follow_request_rejected">%1$s\'s follow request is rejected.</string>
<string name="follow_request_authorized">%1$s\'s follow request is authorized.</string>
</resources>