diff --git a/.idea/dictionaries/tateisu.xml b/.idea/dictionaries/tateisu.xml
index 4749007f..83b1d704 100644
--- a/.idea/dictionaries/tateisu.xml
+++ b/.idea/dictionaries/tateisu.xml
@@ -10,6 +10,7 @@
enty
favourited
firebase
+ foregrounder
gifv
hashtag
hashtags
diff --git a/.idea/misc.xml b/.idea/misc.xml
index fbb68289..5d199810 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 11b52bc2..61122a88 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
+
+
= 26 ){
+ context.startForegroundService( intent );
+ }else{
+ context.startService(intent);
+ }
}
+
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/PollingForegrounder.java b/app/src/main/java/jp/juggler/subwaytooter/PollingForegrounder.java
new file mode 100644
index 00000000..316fa292
--- /dev/null
+++ b/app/src/main/java/jp/juggler/subwaytooter/PollingForegrounder.java
@@ -0,0 +1,107 @@
+package jp.juggler.subwaytooter;
+
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.support.annotation.Nullable;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+
+import jp.juggler.subwaytooter.util.LogCategory;
+import jp.juggler.subwaytooter.util.NotificationHelper;
+
+public class PollingForegrounder extends IntentService {
+
+ static final LogCategory log = new LogCategory( "PollingForegrounder" );
+
+ static final int NOTIFICATION_ID_FOREGROUNDER = 2;
+
+ public PollingForegrounder(){
+ super( "PollingForegrounder" );
+ }
+
+ @Nullable @Override public IBinder onBind( Intent intent ){
+ return null;
+ }
+
+ @Override public void onCreate(){
+ log.d( "onCreate" );
+ super.onCreate();
+
+ // メインスレッド上でPollingWorkerを初期化しておく
+ PollingWorker.getInstance( getApplicationContext() );
+
+ startForeground( NOTIFICATION_ID_FOREGROUNDER, createNotification( getApplicationContext(), "" ) );
+ }
+
+ @Override public void onDestroy(){
+ log.d( "onDestroy" );
+
+ stopForeground( true );
+ super.onDestroy();
+ }
+
+ private Notification createNotification( Context context, String text ){
+ // 通知タップ時のPendingIntent
+ Intent intent_click = new Intent( context, ActMain.class );
+ intent_click.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
+ PendingIntent pi_click = PendingIntent.getActivity( context, 2, intent_click, PendingIntent.FLAG_UPDATE_CURRENT );
+
+ NotificationCompat.Builder builder;
+ if( Build.VERSION.SDK_INT >= 26 ){
+ // Android 8 から、通知のスタイルはユーザが管理することになった
+ // NotificationChannel を端末に登録しておけば、チャネルごとに管理画面が作られる
+ NotificationChannel channel = NotificationHelper.createNotificationChannel(
+ context
+ , "PollingForegrounder" // id
+ , "real-time message notifier" // The user-visible name of the channel.
+ , null // The user-visible description of the channel.
+ , NotificationManager.IMPORTANCE_LOW
+ );
+ builder = new NotificationCompat.Builder( context, channel.getId() );
+ }else{
+ builder = new NotificationCompat.Builder( context, "not_used" );
+ }
+
+ builder
+ .setContentIntent( pi_click )
+ .setAutoCancel( false )
+ .setOngoing( true )
+ .setSmallIcon( R.drawable.ic_notification ) // ここは常に白テーマのアイコンを使う
+ .setColor( ContextCompat.getColor( context, R.color.Light_colorAccent ) ) // ここは常に白テーマの色を使う
+ .setWhen( System.currentTimeMillis() )
+ .setContentTitle( context.getString( R.string.loading_notification_title ) )
+ .setContentText( text )
+ ;
+
+ // Android 7.0 ではグループを指定しないと勝手に通知が束ねられてしまう。
+ // 束ねられた通知をタップしても pi_click が実行されないので困るため、
+ // アカウント別にグループキーを設定する
+ builder.setGroup( context.getPackageName() + ":PollingForegrounder" );
+
+ return builder.build();
+ }
+
+ String last_status = null;
+
+ @Override protected void onHandleIntent( @Nullable Intent intent ){
+ if( intent == null ) return;
+ String tag = intent.getStringExtra( PollingWorker.EXTRA_TAG );
+ final Context context = getApplicationContext();
+ PollingWorker.handleFCMMessage( this, tag, new PollingWorker.JobStatusCallback() {
+ @Override public void onStatus( final String sv ){
+ if( TextUtils.isEmpty( sv ) || sv.equals( last_status ) ) return;
+ last_status = sv;
+ log.d( "onStatus %s",sv );
+ startForeground( NOTIFICATION_ID_FOREGROUNDER, createNotification( context, sv ) );
+ }
+ } );
+ }
+}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/PollingWorker.java b/app/src/main/java/jp/juggler/subwaytooter/PollingWorker.java
index 1025e895..df3c0490 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/PollingWorker.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/PollingWorker.java
@@ -11,6 +11,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Build;
@@ -38,6 +40,7 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -68,6 +71,7 @@ public class PollingWorker {
static final LogCategory log = new LogCategory( "PollingWorker" );
static final int NOTIFICATION_ID = 1;
+ static final int NOTIFICATION_ID_ERROR = 3;
// Notification のJSONObject を日時でソートするためにデータを追加する
static final String KEY_TIME = "<>time";
@@ -125,6 +129,7 @@ public class PollingWorker {
final Context context;
final Handler handler;
final SharedPreferences pref;
+ final ConnectivityManager connectivityManager;
final NotificationManager notification_manager;
final JobScheduler scheduler;
final PowerManager power_manager;
@@ -136,6 +141,7 @@ public class PollingWorker {
log.d( "ctor" );
this.context = c.getApplicationContext();
+ this.connectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE );
this.notification_manager = (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE );
this.scheduler = (JobScheduler) context.getSystemService( Context.JOB_SCHEDULER_SERVICE );
@@ -209,6 +215,7 @@ public class PollingWorker {
public void run(){
log.e( "worker thread start." );
+ job_status.set( "worker thread start." );
while( ! bThreadCancelled.get() ){
JobItem item = null;
try{
@@ -221,15 +228,19 @@ public class PollingWorker {
break;
}
}
+
if( item == null ){
+ job_status.set( "no job to run." );
waitEx( 86400000L );
continue;
}
+ job_status.set( "start job " + item.jobId );
acquirePowerLock();
try{
item.refWorker.set( Worker.this );
item.run();
}finally{
+ job_status.set( "end job " + item.jobId );
item.refWorker.set( null );
releasePowerLock();
}
@@ -237,6 +248,7 @@ public class PollingWorker {
log.trace( ex );
}
}
+ job_status.set( "worker thread end." );
log.e( "worker thread end." );
}
}
@@ -263,7 +275,7 @@ public class PollingWorker {
// JobService#onStartJob から呼ばれる
public boolean onStartJob( @NonNull JobService jobService, @NonNull JobParameters params ){
JobItem item = new JobItem( jobService, params );
- addJob( item );
+ addJob( item, true );
return true;
// return True if your context needs to process the work (on a separate thread).
// return False if there's no more work to be done for this job.
@@ -272,9 +284,7 @@ public class PollingWorker {
// FCMメッセージイベントから呼ばれる
private boolean hasJob( int jobId ){
synchronized( job_list ){
- Iterator< JobItem > it = job_list.iterator();
- while( it.hasNext() ){
- JobItem itemOld = it.next();
+ for( JobItem itemOld : job_list ){
if( itemOld.jobId == jobId ) return true;
}
}
@@ -282,23 +292,25 @@ public class PollingWorker {
}
// FCMメッセージイベントから呼ばれる
- private void addJob( int jobId ){
- addJob( new JobItem( jobId ) );
+ private void addJob( int jobId, boolean bRemoveOld ){
+ addJob( new JobItem( jobId ), bRemoveOld );
}
- private void addJob( @NonNull JobItem item ){
+ private void addJob( @NonNull JobItem item, boolean bRemoveOld ){
int jobId = item.jobId;
// 同じジョブ番号がジョブリストにあるか?
synchronized( job_list ){
- Iterator< JobItem > it = job_list.iterator();
- while( it.hasNext() ){
- JobItem itemOld = it.next();
- if( itemOld.jobId == jobId ){
- log.w( "onStartJob: jobId=%s, old job cancelled." );
- // 同じジョブをすぐに始めるのだからrescheduleはfalse
- itemOld.cancel( false );
- it.remove();
+ if( bRemoveOld ){
+ Iterator< JobItem > it = job_list.iterator();
+ while( it.hasNext() ){
+ JobItem itemOld = it.next();
+ if( itemOld.jobId == jobId ){
+ log.w( "addJob: jobId=%s, old job cancelled.", jobId );
+ // 同じジョブをすぐに始めるのだからrescheduleはfalse
+ itemOld.cancel( false );
+ it.remove();
+ }
}
}
log.d( "addJob: jobId=%s, add to list.", jobId );
@@ -397,11 +409,25 @@ public class PollingWorker {
public void run(){
+ job_status.set( "job start." );
try{
log.d( "(JobItem.run jobId=%s", jobId );
-
if( isJobCancelled() ) throw new JobCancelledException();
+ job_status.set( "check network status.." );
+
+ long net_wait_start = SystemClock.elapsedRealtime();
+ while( ! checkNetwork() ){
+ if( isJobCancelled() ) throw new JobCancelledException();
+ long now = SystemClock.elapsedRealtime();
+ long delta = now - net_wait_start;
+ if( delta >= 10000L ){
+ log.d( "network state timeout." );
+ break;
+ }
+ waitWorkerThread( 333L );
+ }
+
muted_app = MutedApp.getNameSet();
muted_word = MutedWord.getNameSet();
@@ -418,6 +444,7 @@ public class PollingWorker {
// タスクがなかった場合でも定期実行ジョブからの実行ならポーリングを行う
new TaskRunner().runTask( JobItem.this, TASK_POLLING, null );
}
+ job_status.set( "make next schedule." );
if( ! isJobCancelled() && bPollingComplete ){
// ポーリングが完了したのならポーリングが必要かどうかに合わせてジョブのスケジュールを変更する
@@ -447,6 +474,8 @@ public class PollingWorker {
}catch( Throwable ex ){
log.trace( ex );
log.e( ex, "job execution failed." );
+ }finally{
+ job_status.set( "job finished." );
}
// ジョブ終了報告
if( ! isJobCancelled() ){
@@ -474,6 +503,23 @@ public class PollingWorker {
log.d( ")JobItem.run jobId=%s, cancel=%s", jobId, isJobCancelled() );
}
+ private boolean checkNetwork(){
+ NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
+ if( ni == null ){
+ log.d( "checkNetwork: getActiveNetworkInfo() returns null." );
+ return false;
+ }else{
+ NetworkInfo.State state = ni.getState();
+ NetworkInfo.DetailedState detail = ni.getDetailedState();
+ log.d( "checkNetwork: state=%s,detail=%s", state, detail );
+ if( state != NetworkInfo.State.CONNECTED ){
+ log.d( "checkNetwork: not connected." );
+ return false;
+ }else{
+ return true;
+ }
+ }
+ }
}
//////////////////////////////////////////////////////////////////////
@@ -490,13 +536,17 @@ public class PollingWorker {
JobItem job;
int taskId;
+ final ArrayList< String > error_instance = new ArrayList<>();
+
public void runTask( JobItem job, int taskId, JSONObject taskData ){
try{
log.e( "(runTask: taskId=%s", taskId );
+ job_status.set( "start task " + taskId );
+
this.job = job;
this.taskId = taskId;
- long process_db_id = -1L;
+ long process_db_id = - 1L;
if( taskId == TASK_APP_DATA_IMPORT_BEFORE ){
scheduler.cancelAll();
@@ -569,6 +619,8 @@ public class PollingWorker {
loadCustomStreamListenerSetting();
+ job_status.set( "make install id" );
+
// インストールIDを生成する
// インストールID生成時にSavedAccountテーブルを操作することがあるので
// アカウントリストの取得より先に行う
@@ -576,16 +628,19 @@ public class PollingWorker {
job.install_id = getInstallId();
}
+ job_status.set( "create account thread" );
+
LinkedList< AccountThread > thread_list = new LinkedList<>();
for( SavedAccount _a : SavedAccount.loadAccountList( context, log ) ){
if( _a.isPseudo() ) continue;
- if( process_db_id != -1L && _a.db_id != process_db_id ) continue;
+ if( process_db_id != - 1L && _a.db_id != process_db_id ) continue;
AccountThread t = new AccountThread( _a );
thread_list.add( t );
t.start();
}
for( ; ; ){
+ TreeSet< String > set = new TreeSet<>();
Iterator< AccountThread > it = thread_list.iterator();
while( it.hasNext() ){
AccountThread t = it.next();
@@ -593,15 +648,28 @@ public class PollingWorker {
it.remove();
continue;
}
+ set.add( t.account.host );
if( job.isJobCancelled() ){
t.cancel();
}
}
- if( thread_list.isEmpty() ) break;
-
+ int remain = thread_list.size();
+ if( remain <= 0 ) break;
+ //
+ StringBuilder sb = new StringBuilder();
+ for( String s : set ){
+ if( sb.length() > 0 ) sb.append( ", " );
+ sb.append( s );
+ }
+ job_status.set( "waiting " + sb.toString() );
+ //
job.waitWorkerThread( job.isJobCancelled() ? 50L : 1000L );
}
+ synchronized( error_instance ){
+ createErrorNotification( error_instance );
+ }
+
if( ! job.isJobCancelled() ) job.bPollingComplete = true;
}catch( Throwable ex ){
@@ -609,9 +677,67 @@ public class PollingWorker {
log.e( ex, "task execution failed." );
}finally{
log.e( ")runTask: taskId=%s", taskId );
+ job_status.set( "end task " + taskId );
}
}
+ private void createErrorNotification( ArrayList< String > error_instance ){
+ if( error_instance.isEmpty() ){
+ return;
+ }
+
+ // 通知タップ時のPendingIntent
+ Intent intent_click = new Intent( context, ActCallback.class );
+ intent_click.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
+ PendingIntent pi_click = PendingIntent.getActivity( context, 3, intent_click, PendingIntent.FLAG_UPDATE_CURRENT );
+
+ NotificationCompat.Builder builder;
+ if( Build.VERSION.SDK_INT >= 26 ){
+ // Android 8 から、通知のスタイルはユーザが管理することになった
+ // NotificationChannel を端末に登録しておけば、チャネルごとに管理画面が作られる
+ NotificationChannel channel = NotificationHelper.createNotificationChannel(
+ context
+ , "ErrorNotification"
+ , "Error"
+ , null
+ , NotificationManager.IMPORTANCE_LOW
+ );
+
+ builder = new NotificationCompat.Builder( context, channel.getId() );
+ }else{
+ builder = new NotificationCompat.Builder( context, "not_used" );
+ }
+
+ builder
+ .setContentIntent( pi_click )
+ .setAutoCancel( true )
+ .setSmallIcon( R.drawable.ic_notification ) // ここは常に白テーマのアイコンを使う
+ .setColor( ContextCompat.getColor( context, R.color.Light_colorAccent ) ) // ここは常に白テーマの色を使う
+ .setWhen( System.currentTimeMillis() )
+ .setGroup( context.getPackageName() + ":" + "Error" )
+ ;
+
+ {
+ String header = context.getString( R.string.error_notification_title );
+ String summary = context.getString( R.string.error_notification_summary );
+
+ builder
+ .setContentTitle( header )
+ .setContentText( summary + ": " + error_instance.get( 0 ) )
+ ;
+
+ NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle()
+ .setBigContentTitle( header )
+ .setSummaryText( summary );
+ for( int i = 0 ; i < 5 ; ++ i ){
+ if( i >= error_instance.size() ) break;
+ style.addLine( error_instance.get( i ) );
+ }
+ builder.setStyle( style );
+ }
+ notification_manager.notify( NOTIFICATION_ID_ERROR, builder.build() );
+ }
+
void loadCustomStreamListenerSetting(){
mCustomStreamListenerSetting = null;
mCustomStreamListenerSecret = null;
@@ -950,6 +1076,22 @@ public class PollingWorker {
break;
}else{
log.d( "error. %s", result.error );
+
+ String sv = result.error;
+ if( sv.contains( "Timeout" ) ){
+ synchronized( error_instance ){
+ boolean bFound = false;
+ for( String x : error_instance ){
+ if( x.equals( sv ) ){
+ bFound = true;
+ break;
+ }
+ }
+ if( ! bFound ){
+ error_instance.add( sv );
+ }
+ }
+ }
}
}
}
@@ -1356,43 +1498,6 @@ public class PollingWorker {
// FCMメッセージの処理
//
- public static void handleFCMMessage( @NonNull Context context, long time_start, @Nullable String tag ){
- // FirebaseMessagingService#onMessageReceived はバックグラウンドスレッドから実行されるので、少しなら待機してもよい
- // https://firebase.google.com/docs/cloud-messaging/android/receive
- // 10秒を超えるとプロセスごと殺されるかもしれない
-
- // タスクを追加
- JSONObject data = new JSONObject();
- try{
- if( tag != null ) data.putOpt( EXTRA_TAG, tag );
- data.put( EXTRA_TASK_ID, TASK_FCM_MESSAGE );
- }catch( JSONException ignored ){
- }
- task_list.addLast( context, true, data );
-
-
- // JobScheduler 経由ではないがジョブを追加して実行開始
- PollingWorker pw = getInstance( context );
- pw.addJob( JOB_FCM );
-
- for( ; ; ){
- long now = SystemClock.elapsedRealtime();
- if( ! pw.hasJob( JOB_FCM ) ){
- log.d( "handleFCMMessage: JOB_FCM completed. time=%.2f",(now-time_start)/1000f );
- break;
- }
- if( now - time_start >= ( 1000L * 300 ) ){
- log.d( "handleFCMMessage: JOB_FCM timeout. exit onMessageReceived..." );
- break;
- }
- try{
- Thread.sleep( 50L );
- }catch( InterruptedException ex ){
- break;
- }
- }
- }
-
////////////////////////////////////////////////////////////////////////////
// タスクの追加
@@ -1478,4 +1583,55 @@ public class PollingWorker {
public static void queuePackageReplaced( Context context ){
addTask( context, true, TASK_PACKAGE_REPLACED, null );
}
+
+ public interface JobStatusCallback {
+ void onStatus( String sv );
+ }
+
+ static final AtomicReference< String > job_status = new AtomicReference<>( null );
+
+ public static void handleFCMMessage( Context context, String tag, JobStatusCallback callback ){
+ log.d( "handleFCMMessage: start. tag=%s", tag );
+ long time_start = SystemClock.elapsedRealtime();
+
+ callback.onStatus( "=>" );
+
+ // タスクを追加
+ JSONObject data = new JSONObject();
+ try{
+ if( tag != null ) data.putOpt( EXTRA_TAG, tag );
+ data.put( EXTRA_TASK_ID, TASK_FCM_MESSAGE );
+ }catch( JSONException ignored ){
+ }
+ task_list.addLast( context, true, data );
+
+ callback.onStatus( "==>" );
+
+ // 疑似ジョブを開始
+ PollingWorker pw = getInstance( context );
+ pw.addJob( JOB_FCM, false );
+
+ // 疑似ジョブが終了するまで待機する
+ for( ; ; ){
+ // ジョブが完了した?
+ long now = SystemClock.elapsedRealtime();
+ if( ! pw.hasJob( JOB_FCM ) ){
+ log.d( "handleFCMMessage: JOB_FCM completed. time=%.2f", ( now - time_start ) / 1000f );
+ break;
+ }
+ // ジョブの状況を通知する
+ String sv = job_status.get();
+ if( sv == null ) sv = "(null)";
+ callback.onStatus( sv );
+
+ // 少し待機
+ try{
+ Thread.sleep( 50L );
+ }catch( InterruptedException ex ){
+ log.e( ex, "handleFCMMessage: blocking is interrupted." );
+ break;
+ }
+ }
+ }
+
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java
index 0e51deb0..6f17e708 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java
@@ -162,7 +162,7 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
, ActMain.NOT_CROSS_ACCOUNT
, ! status.reblogged
, false
- , bSimpleList ? activity.boost_complete_callback : null
+ , !bSimpleList ? null : status.reblogged ? activity.boost_complete_callback : activity.unboost_complete_callback
);
}
break;
@@ -176,7 +176,7 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
, status
, ActMain.NOT_CROSS_ACCOUNT
, ! status.favourited
- , bSimpleList ? activity.favourite_complete_callback : null
+ , !bSimpleList ? null : status.favourited ? activity.unfavourite_complete_callback : activity.favourite_complete_callback
);
}
break;
diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/NotificationHelper.java b/app/src/main/java/jp/juggler/subwaytooter/util/NotificationHelper.java
index b99d0ed1..e5228257 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/util/NotificationHelper.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/util/NotificationHelper.java
@@ -5,6 +5,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import jp.juggler.subwaytooter.R;
import jp.juggler.subwaytooter.table.SavedAccount;
@@ -15,19 +16,24 @@ public class NotificationHelper {
@TargetApi(26)
public static NotificationChannel createNotificationChannel( @NonNull Context context, @NonNull SavedAccount account ){
-
+ return createNotificationChannel(context
+ ,account.acct
+ ,account.acct
+ ,context.getString( R.string.notification_channel_description, account.acct )
+ ,NotificationManager.IMPORTANCE_DEFAULT // : NotificationManager.IMPORTANCE_LOW;
+ );
+ }
+
+ @TargetApi(26)
+ public static NotificationChannel createNotificationChannel(
+ @NonNull Context context
+ , @NonNull String channel_id // id
+ , @NonNull String name // The user-visible name of the channel.
+ , @Nullable String description // The user-visible description of the channel.
+ , int importance
+ ){
NotificationManager notification_manager = (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE );
- // The id of the channel.
- String channel_id = account.acct;
-
- // The user-visible name of the channel.
- CharSequence name = context.getString( R.string.notification_for, account.acct );
-
- // The user-visible description of the channel.
- String description = context.getString( R.string.notification_channel_description, account.acct );
- //
- int importance = NotificationManager.IMPORTANCE_DEFAULT; // : NotificationManager.IMPORTANCE_LOW;
//
NotificationChannel channel = null;
try{
@@ -39,7 +45,8 @@ public class NotificationHelper {
channel = new NotificationChannel( channel_id, name, importance );
}
channel.setName( name );
- channel.setDescription( description );
+ channel.setImportance( importance );
+ if( description != null ) channel.setDescription( description );
notification_manager.createNotificationChannel( channel );
return channel;
}
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 799f88b2..03ea692f 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -51,6 +51,7 @@
Partager
Partager à partir d\'un autre compte
Partagé avec succès
+ boost removed
Partagé par …
Couleur d\'arrière plan des onglets
Couleur des onglets
@@ -132,6 +133,7 @@
Favori
Placer dans les favoris à partir d\'un autre compte
Mis en favori avec succès
+ Mis en unfavori avec succès
Mis en favori par …
Favoris
Chronologie fédérée
@@ -468,6 +470,9 @@
\"%1$s\" is too long (%2$d/%3$d characters).\nIt is not acceptable in the standard instance, but it may be acceptable in some instances.\nAre you sure?
Avatar icon size (unit:dp. default:48. app restart required)
Short acct for local user (app restart required)
+ Loading notification…
+ Server Timeout
+ If it\'s dead instance, please remove account on that server.
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 0cf3835b..d36df853 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -369,8 +369,10 @@
アプリ: %1$s
ブーストできました
+ ブースト解除しました
お気に入りにしました
+ お気に入り解除しました
スワイプで解除。解除した後はカラムをリロードすると再表示されます
@@ -755,5 +757,7 @@
\"%1$s\"が長すぎます(%2$d/%3$d文字).\n標準的なインスタンスではエラーとなりますが、いくつかのインスタンスでは許容されるかも。\nよろしいですか?
アバターアイコンサイズ(単位:dp. デフォルト:48. アプリ再起動が必要)
ローカルユーザのAcct表記を短くする(アプリ再起動が必要)
-
+ 通知の取得中…
+ サーバータイムアウト
+ もし死んだインスタンスなら、そのサーバ上のアカウントを除去してください
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bdcc474a..b2e03fef 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -190,7 +190,9 @@
mute app \"%1$s\"
App was muted.
favourite succeeded
+ favourite removed
boost succeeded
+ boost removed
notification option
sound
vibration
@@ -462,5 +464,8 @@
\"%1$s\" is too long (%2$d/%3$d characters).\nIt is not acceptable in the standard instance, but it may be acceptable in some instances.\nAre you sure?
Avatar icon size (unit:dp. default:48. app restart required)
Short acct for local user (app restart required)
+ Loading notification…
+ Server Timeout
+ If it\'s dead instance, please remove account on that server.