diff --git a/app/build.gradle b/app/build.gradle
index 101d6b96..ad8a4e93 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,8 +9,8 @@ android {
applicationId "jp.juggler.subwaytooter"
minSdkVersion 21
targetSdkVersion 25
- versionCode 8
- versionName "0.0.8"
+ versionCode 10
+ versionName "0.1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2b9dab99..8916e6a1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,8 @@
package="jp.juggler.subwaytooter">
+
+
+
+
+
+
+
+
+
+
task = new AsyncTask< Void, String, TootApiResult >() {
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java b/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java
index 81db2386..ea7ae1cd 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java
@@ -47,8 +47,7 @@ public class ActAppSetting extends AppCompatActivity implements CompoundButton.O
}
private void saveUIToData(){
- pref
- .edit()
+ pref.edit()
.putBoolean( Pref.KEY_BACK_TO_COLUMN_LIST, swBackToColumnList.isChecked() )
.putBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, swDontConfirmBeforeCloseColumn.isChecked() )
.apply();
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java
index f7f46516..36c8f653 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java
@@ -80,29 +80,16 @@ public class ActMain extends AppCompatActivity
initUI();
- Uri uri = ActOAuthCallback.last_uri.get();
- if( uri != null ){
- updateAccessToken( uri );
- }
+ AlarmService.startCheck( this );
loadColumnList();
}
- @Override protected void onNewIntent( Intent intent ){
- super.onNewIntent( intent );
- Uri uri = ActOAuthCallback.last_uri.get();
- if( uri != null ){
- updateAccessToken( uri );
- }
- }
-
- @Override
- protected void onDestroy(){
+ @Override protected void onDestroy(){
super.onDestroy();
}
- @Override
- protected void onResume(){
+ @Override protected void onResume(){
super.onResume();
HTMLDecoder.link_callback = link_click_listener;
@@ -122,6 +109,7 @@ public class ActMain extends AppCompatActivity
}
if( bRemoved ){
pager_adapter.setOrder( pager, new_order );
+ saveColumnList();
}
}
@@ -136,12 +124,16 @@ public class ActMain extends AppCompatActivity
if( pager_adapter.getCount() == 0 ){
llEmpty.setVisibility( View.VISIBLE );
}
+
+ Uri uri = ActOAuthCallback.last_uri.get();
+ if( uri != null ){
+ ActOAuthCallback.last_uri.set( null );
+ updateAccessToken( uri );
+ }
}
- @Override
- protected void onPause(){
+ @Override protected void onPause(){
HTMLDecoder.link_callback = null;
- saveColumnList();
super.onPause();
}
@@ -164,6 +156,7 @@ public class ActMain extends AppCompatActivity
ArrayList< Integer > order = data.getIntegerArrayListExtra( ActColumnList.EXTRA_ORDER );
if( order != null && isOrderChanged( order ) ){
pager_adapter.setOrder( pager, order );
+ saveColumnList();
}
if( pager_adapter.column_list.isEmpty() ){
@@ -413,6 +406,25 @@ public class ActMain extends AppCompatActivity
private void updateAccessToken( final Uri uri ){
+ // 通知タップ
+ // subwaytooter://notification_click?db_id=(db_id)
+ String sv = uri.getQueryParameter( "db_id" );
+ if( ! TextUtils.isEmpty( sv ) ){
+ try{
+ long db_id = Long.parseLong( sv, 10 );
+ SavedAccount account = SavedAccount.loadAccount( log, db_id );
+ if( account != null ){
+ Column column = addColumn( account, Column.TYPE_NOTIFICATIONS );
+ if( ! column.bInitialLoading ){
+ column.reload();
+ }
+ }
+ }catch( Throwable ex ){
+ ex.printStackTrace();
+ }
+ return;
+ }
+
final ProgressDialog progress = new ProgressDialog( ActMain.this );
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
@@ -536,7 +548,8 @@ public class ActMain extends AppCompatActivity
this.row_id = SavedAccount.insert( host, user, result.object, result.token_info );
SavedAccount account = SavedAccount.loadAccount( log, row_id );
if( account != null ){
- ActMain.this.onAccountUpdated( account );
+ AlarmService.startCheck( ActMain.this );
+ onAccountUpdated( account );
}
}
}
@@ -580,6 +593,7 @@ public class ActMain extends AppCompatActivity
int page_showing = pager.getCurrentItem();
int page_delete = pager_adapter.column_list.indexOf( column );
pager_adapter.removeColumn( pager, column );
+ saveColumnList();
if( pager_adapter.getCount() == 0 ){
llEmpty.setVisibility( View.VISIBLE );
}else if( page_showing > 0 && page_showing == page_delete ){
@@ -590,12 +604,12 @@ public class ActMain extends AppCompatActivity
//////////////////////////////////////////////////////////////
// カラム追加系
- public void addColumn( SavedAccount ai, int type, Object... params ){
+ public Column addColumn( SavedAccount ai, int type, Object... params ){
// 既に同じカラムがあればそこに移動する
for( Column column : pager_adapter.column_list ){
if( column.isSameSpec( ai, type, params ) ){
pager.setCurrentItem( pager_adapter.column_list.indexOf( column ), true );
- return;
+ return column;
}
}
//
@@ -603,7 +617,9 @@ public class ActMain extends AppCompatActivity
//
Column col = new Column( ActMain.this, ai, type, params );
int idx = pager_adapter.addColumn( pager, col );
+ saveColumnList();
pager.setCurrentItem( idx, true );
+ return col;
}
private void onAccountUpdated( SavedAccount data ){
@@ -641,7 +657,7 @@ public class ActMain extends AppCompatActivity
//////////////////////////////////////////////////////////////
- public interface GetAccountCallback {
+ interface GetAccountCallback {
// return account information
// if failed, account is null.
void onGetAccount( TootAccount account );
@@ -1100,7 +1116,7 @@ public class ActMain extends AppCompatActivity
////////////////////////////////////////
private void performAccountSetting(){
- AccountPicker.pick( this, true,new AccountPicker.AccountPickerCallback() {
+ AccountPicker.pick( this, true, new AccountPicker.AccountPickerCallback() {
@Override
public void onAccountPicked( SavedAccount ai ){
ActAccountSetting.open( ActMain.this, ai, REQUEST_CODE_ACCOUNT_SETTING );
@@ -1137,6 +1153,13 @@ public class ActMain extends AppCompatActivity
startActivityForResult( intent, REQUEST_CODE_COLUMN_LIST );
}
+ private void dumpColumnList(){
+ for( int i = 0, ie = pager_adapter.column_list.size() ; i < ie ; ++ i ){
+ Column column = pager_adapter.column_list.get( i );
+ log.d( "dumpColumnList [%s]%s %s", i, column.access_info.acct, column.getColumnName( true ) );
+ }
+ }
+
static final String FILE_COLUMN_LIST = "column_list";
private void saveColumnList(){
@@ -1170,7 +1193,7 @@ public class ActMain extends AppCompatActivity
try{
JSONObject src = array.optJSONObject( i );
Column col = new Column( ActMain.this, src );
- pager_adapter.addColumn( pager, col );
+ pager_adapter.addColumn( pager, col, pager_adapter.getCount() );
}catch( Throwable ex ){
ex.printStackTrace();
}
@@ -1191,7 +1214,7 @@ public class ActMain extends AppCompatActivity
////////////////////////////////////////////////////////////////////////////
- public interface RelationChangedCallback {
+ interface RelationChangedCallback {
// void onRelationChanged( TootRelationShip relationship );
void onRelationChanged();
}
@@ -1226,8 +1249,8 @@ public class ActMain extends AppCompatActivity
if( result.object != null ){
remote_who = TootAccount.parse( log, access_info, result.object );
- Utils.showToast( ActMain.this, false, bFollow ? R.string.follow_succeeded : R.string.unfollow_succeeded );
- }else if( bFollow && who.locked && result.response.code() == 422 ){
+ Utils.showToast( ActMain.this, false, R.string.follow_succeeded );
+ }else if( who.locked && result.response.code() == 422 ){
Utils.showToast( ActMain.this, false, R.string.cant_follow_locked_user );
}else{
Utils.showToast( ActMain.this, false, result.error );
@@ -1351,7 +1374,7 @@ public class ActMain extends AppCompatActivity
// アカウントを選択してからユーザをフォローする
void followFromAnotherAccount( final SavedAccount access_info, final TootAccount who, final RelationChangedCallback callback ){
- AccountPicker.pick( ActMain.this, false,new AccountPicker.AccountPickerCallback() {
+ AccountPicker.pick( ActMain.this, false, new AccountPicker.AccountPickerCallback() {
@Override
public void onAccountPicked( SavedAccount ai ){
String acct = who.acct;
@@ -1502,9 +1525,8 @@ public class ActMain extends AppCompatActivity
}.execute();
}
- public interface ReportCompleteCallback {
+ interface ReportCompleteCallback {
void onReportComplete( TootApiResult result );
-
}
private void callReport( final SavedAccount account, final TootAccount who, final TootStatus status
@@ -1605,7 +1627,7 @@ public class ActMain extends AppCompatActivity
if( ! tmp_list.isEmpty() ){
dialog.addAction( getString( R.string.favourite_from_another_account ), new Runnable() {
@Override public void run(){
- AccountPicker.pick( ActMain.this, false,tmp_list, new AccountPicker.AccountPickerCallback() {
+ AccountPicker.pick( ActMain.this, false, tmp_list, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
if( ai != null ) performFavourite( ai, status );
}
@@ -1614,7 +1636,7 @@ public class ActMain extends AppCompatActivity
} );
dialog.addAction( getString( R.string.boost_from_another_account ), new Runnable() {
@Override public void run(){
- AccountPicker.pick( ActMain.this,false, tmp_list, new AccountPicker.AccountPickerCallback() {
+ AccountPicker.pick( ActMain.this, false, tmp_list, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
if( ai != null ) performBoost( ai, status, false );
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/AlarmReceiver.java b/app/src/main/java/jp/juggler/subwaytooter/AlarmReceiver.java
new file mode 100644
index 00000000..64982532
--- /dev/null
+++ b/app/src/main/java/jp/juggler/subwaytooter/AlarmReceiver.java
@@ -0,0 +1,19 @@
+package jp.juggler.subwaytooter;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.content.WakefulBroadcastReceiver;
+
+
+public class AlarmReceiver extends WakefulBroadcastReceiver {
+
+ static final String EXTRA_RECEIVED_INTENT = "received_intent";
+ static final String ACTION_FROM_RECEIVER = "from_receiver";
+
+ @Override public void onReceive( Context context, Intent intent ){
+ Intent serviceIntent = new Intent(context,AlarmService.class);
+ serviceIntent.setAction( ACTION_FROM_RECEIVER );
+ serviceIntent.putExtra(EXTRA_RECEIVED_INTENT,intent);
+ startWakefulService(context,serviceIntent);
+ }
+}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/AlarmService.java b/app/src/main/java/jp/juggler/subwaytooter/AlarmService.java
new file mode 100644
index 00000000..9d56a08b
--- /dev/null
+++ b/app/src/main/java/jp/juggler/subwaytooter/AlarmService.java
@@ -0,0 +1,493 @@
+package jp.juggler.subwaytooter;
+
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.WakefulBroadcastReceiver;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import jp.juggler.subwaytooter.api.TootApiClient;
+import jp.juggler.subwaytooter.api.TootApiResult;
+import jp.juggler.subwaytooter.api.entity.TootNotification;
+import jp.juggler.subwaytooter.table.NotificationTracking;
+import jp.juggler.subwaytooter.table.SavedAccount;
+import jp.juggler.subwaytooter.util.LogCategory;
+import jp.juggler.subwaytooter.util.Utils;
+
+public class AlarmService extends IntentService {
+
+ static final LogCategory log = new LogCategory( "AlarmService" );
+
+ // PendingIntent の request code
+ static final int PENDING_CODE_ALARM = 1;
+ static final String ACTION_NOTIFICATION_DELETE = "notification_delete";
+ static final String ACTION_NOTIFICATION_CLICK = "notification_click";
+ static final int NOTIFICATION_ID = 1;
+ static final long INTERVAL_MIN = 60000L * 5;
+
+ // Notifiation のJSONObject を日時でソートするためにデータを追加する
+ static final String KEY_TIME = "<>time";
+ private static final String ACTION_DATA_INJECTED = "data_injected";
+ private static final String EXTRA_DB_ID = "db_id";
+
+ public AlarmService(){
+ // name: Used to name the worker thread, important only for debugging.
+ super( "AlarmService" );
+ }
+
+ AlarmManager alarm_manager;
+ PowerManager power_manager;
+ NotificationManager notification_manager;
+ PowerManager.WakeLock wake_lock;
+ PendingIntent pi_next;
+
+ @Override public void onCreate(){
+ super.onCreate();
+ log.d("ctor");
+
+ alarm_manager = (AlarmManager) getApplicationContext().getSystemService( ALARM_SERVICE );
+ power_manager = (PowerManager) getApplicationContext().getSystemService( POWER_SERVICE );
+ notification_manager = (NotificationManager) getApplicationContext().getSystemService( NOTIFICATION_SERVICE );
+
+ wake_lock = power_manager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, AlarmService.class.getName() );
+ wake_lock.setReferenceCounted( false );
+ wake_lock.acquire();
+
+ // 次回レシーバーを起こすためのPendingIntent
+ Intent next_intent = new Intent( this, AlarmReceiver.class );
+ pi_next = PendingIntent.getBroadcast( this, PENDING_CODE_ALARM, next_intent, PendingIntent.FLAG_UPDATE_CURRENT );
+
+ }
+
+ @Override public void onDestroy(){
+ log.d("dtor");
+ wake_lock.release();
+
+ super.onDestroy();
+ }
+
+ // IntentService は onHandleIntent をワーカースレッドから呼び出す
+ // 同期処理を行って良い
+ @Override protected void onHandleIntent( @Nullable Intent intent ){
+
+ ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( log );
+
+ if( intent != null ){
+ String action = intent.getAction();
+ log.d("onHandleIntent action=%s",action);
+
+ if( ACTION_DATA_INJECTED.equals( action ) ){
+ processInjectedData();
+ }else if( AlarmReceiver.ACTION_FROM_RECEIVER.equals( action ) ){
+ WakefulBroadcastReceiver.completeWakefulIntent( intent );
+ //
+ Intent received_intent = intent.getParcelableExtra( AlarmReceiver.EXTRA_RECEIVED_INTENT );
+ if( received_intent != null ){
+
+ action = received_intent.getAction();
+ 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);
+ 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);
+ NotificationTracking.updateRead( db_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.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
+ startActivity( intent );
+ return;
+
+ }
+ }
+ }
+ }
+
+ TootApiClient client = new TootApiClient( this, new TootApiClient.Callback() {
+ @Override public boolean isApiCancelled(){
+ return false;
+ }
+
+ @Override public void publishApiProgress( String s ){
+
+ }
+ } );
+
+ boolean bAlarmRequired = false;
+ if( account_list != null ){
+ for( SavedAccount account : account_list ){
+ try{
+ if( account.notification_mention
+ || account.notification_boost
+ || account.notification_favourite
+ || account.notification_follow
+ ){
+ bAlarmRequired = true;
+
+ ArrayList< Data > data_list = new ArrayList<>();
+
+ checkAccount( client, data_list, account );
+
+ showNotification( account.db_id, data_list );
+
+ }
+ }catch( Throwable ex ){
+ ex.printStackTrace();
+ }
+ }
+ }
+
+
+
+ alarm_manager.cancel( pi_next );
+ if( bAlarmRequired ){
+ long now = SystemClock.elapsedRealtime();
+ alarm_manager.setWindow(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP
+ , now + INTERVAL_MIN
+ , 60000L * 10
+ , pi_next
+ );
+ log.d("alarm set!");
+ }else{
+ log.d("alarm is no longer required.");
+ }
+ }
+
+
+
+ private static class Data {
+ SavedAccount access_info;
+ TootNotification notification;
+ }
+
+ private static final String PATH_NOTIFICATIONS = "/api/v1/notifications";
+
+ private void checkAccount( TootApiClient client, ArrayList< Data > data_list, SavedAccount account ){
+ NotificationTracking nr = NotificationTracking.load( account.db_id );
+
+ // まずキャッシュされたデータを処理する
+ HashSet< Long > duplicate_check = new HashSet<>();
+ ArrayList< JSONObject > dst_array = new ArrayList<>();
+ if( nr.last_data != null ){
+ try{
+ 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 );
+ }
+ }catch( JSONException ex ){
+ ex.printStackTrace();
+ }
+ }
+
+ // 前回の更新から一定時刻が経過したら新しいデータを注ぎ足す
+ long now = System.currentTimeMillis();
+ if( now - nr.last_load >= INTERVAL_MIN ){
+ nr.last_load = now;
+
+ client.setAccount( account );
+
+ for( int nTry = 0 ; nTry < 4 ; ++ nTry ){
+ TootApiResult result = client.request( PATH_NOTIFICATIONS );
+ if( result == null ){
+ log.d( "cancelled." );
+ break;
+ }else if( result.array != null ){
+ try{
+ 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 );
+ }
+ }catch( JSONException ex ){
+ ex.printStackTrace();
+ }
+ break;
+ }else{
+ log.d( "error. %s", result.error );
+ }
+ }
+ }
+
+ Collections.sort( dst_array, new Comparator< JSONObject >() {
+ @Override public int compare( JSONObject a, JSONObject b ){
+ long la = a.optLong( KEY_TIME, 0 );
+ long lb = b.optLong( KEY_TIME, 0 );
+ // 新しい順
+ if( la < lb ) return + 1;
+ if( la > lb ) return - 1;
+ return 0;
+ }
+ } );
+
+ JSONArray d = new JSONArray();
+ for( int i = 0 ; i < 10 ; ++ i ){
+ if( i >= dst_array.size() ) break;
+ d.put( dst_array.get( i ) );
+ }
+ nr.last_data = d.toString();
+ nr.save();
+ }
+
+ void update_sub(
+ JSONObject src
+ , NotificationTracking nr
+ , SavedAccount account
+ , ArrayList< JSONObject > dst_array
+ , ArrayList< Data > data_list
+ , HashSet< Long > duplicate_check
+ ) throws JSONException{
+
+ long id = src.optLong( "id" );
+
+ if( duplicate_check.contains( id ) ) return;
+ duplicate_check.add( id );
+
+ String type = Utils.optStringX( src, "type" );
+
+ if( id <= nr.nid_read ){
+ return;
+ }else if( id > nr.nid_show ){
+ // 種別チェックより先に「表示済み」idの更新を行う
+ nr.nid_show = id;
+ }
+
+ if( ( ! account.notification_mention && TootNotification.TYPE_MENTION.equals( type ) )
+ || ( ! account.notification_boost && TootNotification.TYPE_REBLOG.equals( type ) )
+ || ( ! account.notification_favourite && TootNotification.TYPE_FAVOURITE.equals( type ) )
+ || ( ! account.notification_follow && TootNotification.TYPE_FOLLOW.equals( type ) )
+ ){
+ return;
+ }
+
+ //
+ Data data = new Data();
+ data.access_info = account;
+ data.notification = TootNotification.parse( log, account, src );
+ if( data.notification != null ){
+ data_list.add( data );
+ //
+ src.put( KEY_TIME, data.notification.time_created_at );
+ dst_array.add( src );
+ }
+ }
+
+ public String getNotificationLine( String type, CharSequence display_name ){
+ if( TootNotification.TYPE_FAVOURITE.equals( type ) ){
+ 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 );
+ }
+ if( TootNotification.TYPE_MENTION.equals( type ) ){
+ 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 "- "+"?";
+ }
+
+ 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 );
+ return;
+ }
+
+ Collections.sort( data_list, new Comparator< Data >() {
+ @Override public int compare( Data a, Data b ){
+ long la = a.notification.time_created_at;
+ long lb = b.notification.time_created_at;
+ // 新しい順
+ if( la < lb ) return + 1;
+ if( la > lb ) return - 1;
+ return 0;
+ }
+ } );
+
+ Data item = data_list.get( 0 );
+ NotificationTracking nt = NotificationTracking.load( account_db_id );
+ if( item.notification.time_created_at == nt.post_time
+ && item.notification.id == nt.post_id
+ ){
+ // 先頭にあるデータが同じなら、通知を更新しない
+ // このマーカーは端末再起動時にリセットされるので、再起動後は通知が出るはず
+ return;
+ }
+ 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.setAction( ACTION_NOTIFICATION_CLICK );
+
+ Intent intent_delete = new Intent( this, AlarmReceiver.class );
+ 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
+ PendingIntent pi_delete = PendingIntent.getBroadcast( this, (Integer.MAX_VALUE-(int)account_db_id), intent_delete, PendingIntent.FLAG_UPDATE_CURRENT );
+
+ 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.colorAccent ) )
+ .setDefaults( NotificationCompat.DEFAULT_ALL )
+ .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 );
+ if( data_list.size() == 1 ){
+ builder.setContentTitle( a );
+ builder.setContentText( acct );
+ }else{
+ String header = getString( R.string.notification_count, data_list.size() );
+ builder.setContentTitle( header )
+ .setContentText( a );
+
+ NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle()
+ .setBigContentTitle( header )
+ .setSummaryText( acct );
+ for( int i = 0 ; i < 5 ; ++ i ){
+ if( i >= data_list.size() ) break;
+ item = data_list.get( i );
+ a = getNotificationLine( item.notification.type, item.notification.account.display_name );
+ style.addLine( a );
+ }
+ builder.setStyle( style );
+ }
+
+ notification_manager.notify( notification_tag,NOTIFICATION_ID, builder.build() );
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Activity との連携
+
+ public static void startCheck(Context context){
+ Intent intent = new Intent(context,AlarmReceiver.class);
+ context.sendBroadcast( intent );
+ }
+
+ private static class InjectData {
+ long account_db_id;
+ TootNotification.List list = new TootNotification.List();
+ }
+
+ static final ConcurrentLinkedQueue< InjectData > inject_queue = new ConcurrentLinkedQueue<>();
+
+ public static void injectData( Context context, long account_db_id, TootNotification.List src ){
+ InjectData data = new InjectData();
+ data.account_db_id = account_db_id;
+ data.list.addAll( src );
+ inject_queue.add( data );
+
+ Intent intent = new Intent( context, AlarmService.class );
+ intent.setAction( ACTION_DATA_INJECTED );
+ context.startService( intent );
+ }
+
+ private void processInjectedData(){
+ while( inject_queue.size() > 0 ){
+
+ InjectData data = inject_queue.poll();
+
+ SavedAccount account = SavedAccount.loadAccount( log, data.account_db_id );
+ if( account == null ) continue;
+
+ NotificationTracking nr = NotificationTracking.load( data.account_db_id );
+
+ HashSet< Long > duplicate_check = new HashSet<>();
+
+ ArrayList< JSONObject > dst_array = new ArrayList<>();
+ if( nr.last_data != null ){
+ // まずキャッシュされたデータを処理する
+ try{
+ JSONArray array = new JSONArray( nr.last_data );
+ for( int i = array.length() - 1 ; i >= 0 ; -- i ){
+ JSONObject src = array.optJSONObject( i );
+ dst_array.add( src );
+ duplicate_check.add( src.optLong( "id" ) );
+ }
+ }catch( JSONException ex ){
+ ex.printStackTrace();
+ }
+ }
+ for( TootNotification item : data.list ){
+ try{
+ if( duplicate_check.contains( item.id ) ) continue;
+ duplicate_check.add( item.id );
+
+ String type = item.type;
+
+ if( ( ! account.notification_mention && TootNotification.TYPE_MENTION.equals( type ) )
+ || ( ! account.notification_boost && TootNotification.TYPE_REBLOG.equals( type ) )
+ || ( ! account.notification_favourite && TootNotification.TYPE_FAVOURITE.equals( type ) )
+ || ( ! account.notification_follow && TootNotification.TYPE_FOLLOW.equals( type ) )
+ ){
+ continue;
+ }
+
+ //
+ JSONObject src = item.json;
+ src.put( KEY_TIME, item.time_created_at );
+ dst_array.add( src );
+ }catch( JSONException ex ){
+ ex.printStackTrace();
+ }
+ }
+
+ // 新しい順にソート
+ Collections.sort( dst_array, new Comparator< JSONObject >() {
+ @Override public int compare( JSONObject a, JSONObject b ){
+ long la = a.optLong( KEY_TIME, 0 );
+ long lb = b.optLong( KEY_TIME, 0 );
+ // 新しい順
+ if( la < lb ) return + 1;
+ if( la > lb ) return - 1;
+ return 0;
+ }
+ } );
+
+ // 最新10件を保存
+ JSONArray d = new JSONArray();
+ for( int i = 0 ; i < 10 ; ++ i ){
+ if( i >= dst_array.size() ) break;
+ d.put( dst_array.get( i ) );
+ }
+ nr.last_data = d.toString();
+ nr.save();
+ }
+ }
+}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/App1.java b/app/src/main/java/jp/juggler/subwaytooter/App1.java
index 24d25078..6670db5f 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/App1.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/App1.java
@@ -19,6 +19,7 @@ import jp.juggler.subwaytooter.table.ClientInfo;
import jp.juggler.subwaytooter.table.ContentWarning;
import jp.juggler.subwaytooter.table.LogData;
import jp.juggler.subwaytooter.table.MediaShown;
+import jp.juggler.subwaytooter.table.NotificationTracking;
import jp.juggler.subwaytooter.table.SavedAccount;
import okhttp3.OkHttpClient;
import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
@@ -26,38 +27,11 @@ import uk.co.chrisjenx.calligraphy.TypefaceUtils;
public class App1 extends Application {
- @Override
- public void onCreate(){
- super.onCreate();
-
- CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
- .setFontAttrId(R.attr.fontPath)
- .build()
- );
-
- if( typeface_emoji == null ){
- typeface_emoji = TypefaceUtils.load(getAssets(), "emojione_android.ttf");
- }
-
- if( db_open_helper == null ){
- db_open_helper = new DBOpenHelper( getApplicationContext() );
- }
-
- if( image_loader == null ){
- image_loader = new MyImageLoader(
- Volley.newRequestQueue( getApplicationContext() )
- , new BitmapCache()
- );
- }
- }
-
- @Override
- public void onTerminate(){
- super.onTerminate();
- }
static final String DB_NAME = "app_db";
- static final int DB_VERSION = 1;
+ static final int DB_VERSION = 2;
+ // 2017/4/25 v10 1=>2 SavedAccount に通知設定を追加
+ // 2017/4/25 v10 1=>2 NotificationTracking テーブルを追加
static DBOpenHelper db_open_helper;
@@ -65,9 +39,10 @@ public class App1 extends Application {
return db_open_helper.getWritableDatabase();
}
- static class DBOpenHelper extends SQLiteOpenHelper {
+
+ private static class DBOpenHelper extends SQLiteOpenHelper {
- public DBOpenHelper( Context context ){
+ DBOpenHelper( Context context ){
super( context, DB_NAME, null, DB_VERSION );
}
@@ -79,6 +54,7 @@ public class App1 extends Application {
ClientInfo.onDBCreate( db );
MediaShown.onDBCreate(db);
ContentWarning.onDBCreate(db);
+ NotificationTracking.onDBCreate(db);
}
@Override
@@ -89,6 +65,7 @@ public class App1 extends Application {
ClientInfo.onDBUpgrade( db, oldVersion, newVersion );
MediaShown.onDBUpgrade( db, oldVersion, newVersion );
ContentWarning.onDBUpgrade( db, oldVersion, newVersion );
+ NotificationTracking.onDBUpgrade( db, oldVersion, newVersion );
}
}
@@ -98,7 +75,7 @@ public class App1 extends Application {
return image_loader;
}
- public static class MyImageLoader extends ImageLoader {
+ private static class MyImageLoader extends ImageLoader {
/**
* Constructs a new ImageLoader.
@@ -122,7 +99,7 @@ public class App1 extends Application {
}
}
- public static class BitmapCache implements ImageLoader.ImageCache {
+ private static class BitmapCache implements ImageLoader.ImageCache {
private LruCache mCache;
@@ -154,4 +131,38 @@ public class App1 extends Application {
// public static final RelationshipMap relationship_map = new RelationshipMap();
+ @Override
+ public void onCreate(){
+ super.onCreate();
+
+ CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
+ .setFontAttrId(R.attr.fontPath)
+ .build()
+ );
+
+ if( typeface_emoji == null ){
+ typeface_emoji = TypefaceUtils.load(getAssets(), "emojione_android.ttf");
+ }
+
+ if( db_open_helper == null ){
+ db_open_helper = new DBOpenHelper( getApplicationContext() );
+
+ if( BuildConfig.DEBUG){
+// SQLiteDatabase db = db_open_helper.getWritableDatabase();
+// db_open_helper.onCreate( db );
+ }
+ }
+
+ if( image_loader == null ){
+ image_loader = new MyImageLoader(
+ Volley.newRequestQueue( getApplicationContext() )
+ , new BitmapCache()
+ );
+ }
+ }
+
+ @Override
+ public void onTerminate(){
+ super.onTerminate();
+ }
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.java b/app/src/main/java/jp/juggler/subwaytooter/Column.java
index a391beee..311170b4 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/Column.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/Column.java
@@ -1,5 +1,6 @@
package jp.juggler.subwaytooter;
+import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
@@ -72,9 +73,9 @@ class Column {
static final String KEY_COLUMN_NAME = "column_name";
static final String KEY_OLD_INDEX = "old_index";
- private final ActMain activity;
+ private final @NonNull ActMain activity;
- final SavedAccount access_info;
+ final @NonNull SavedAccount access_info;
final int type;
static final int TYPE_HOME = 1;
@@ -82,7 +83,7 @@ class Column {
static final int TYPE_FEDERATE = 3;
static final int TYPE_PROFILE = 4;
static final int TYPE_FAVOURITES = 5;
- static final int TYPE_REPORTS = 6;
+ private static final int TYPE_REPORTS = 6;
static final int TYPE_NOTIFICATIONS = 7;
static final int TYPE_CONVERSATION = 8;
static final int TYPE_HASHTAG = 9;
@@ -106,7 +107,7 @@ class Column {
int scroll_pos;
int scroll_y;
- Column( ActMain activity, @NonNull SavedAccount access_info, int type, Object... params ){
+ Column( @NonNull ActMain activity, @NonNull SavedAccount access_info, int type, Object... params ){
this.activity = activity;
this.access_info = access_info;
this.type = type;
@@ -155,10 +156,13 @@ class Column {
item.put( KEY_OLD_INDEX, old_index );
}
- Column( ActMain activity, JSONObject src ){
+ Column( @NonNull ActMain activity, JSONObject src ){
this.activity = activity;
- this.access_info = SavedAccount.loadAccount( log, src.optLong( KEY_ACCOUNT_ROW_ID ) );
- if( access_info == null ) throw new RuntimeException( "missing account" );
+
+ SavedAccount ac = SavedAccount.loadAccount( log, src.optLong( KEY_ACCOUNT_ROW_ID ) );
+ if( ac == null ) throw new RuntimeException( "missing account" );
+ this.access_info = ac;
+
this.type = src.optInt( KEY_TYPE );
switch( type ){
case TYPE_CONVERSATION:
@@ -452,8 +456,14 @@ class Column {
TootApiResult parseNotifications( TootApiResult result ){
if( result != null ){
saveRange( result, true, true );
- list_tmp = new ArrayList<>();
- list_tmp.addAll( TootNotification.parseList( log, access_info, result.array ) );
+ TootNotification.List src= TootNotification.parseList( log, access_info, result.array );
+ if( src != null){
+ list_tmp = new ArrayList<>();
+ list_tmp.addAll( src );
+ //
+ AlarmService.injectData( activity,access_info.db_id, src );
+ }
+
}
return result;
}
@@ -594,6 +604,8 @@ class Column {
if( list_tmp != null ){
list_data.clear();
list_data.addAll( list_tmp );
+
+
}
}
@@ -700,8 +712,14 @@ class Column {
TootApiResult parseNotifications( TootApiResult result ){
if( result != null ){
saveRange( result, bBottom, ! bBottom );
- list_tmp = new ArrayList<>();
- list_tmp.addAll( TootNotification.parseList( log, access_info, result.array ) );
+
+ TootNotification.List src = TootNotification.parseList( log, access_info, result.array );
+ if( src != null ){
+ list_tmp = new ArrayList<>();
+ list_tmp.addAll( src );
+ //
+ AlarmService.injectData( activity,access_info.db_id, src );
+ }
}
return result;
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/Pref.java b/app/src/main/java/jp/juggler/subwaytooter/Pref.java
index 9123bd56..e6aac280 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/Pref.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/Pref.java
@@ -6,7 +6,8 @@ import android.preference.PreferenceManager;
public class Pref {
- public static SharedPreferences pref(Context context){
+
+ public static SharedPreferences pref( Context context){
return PreferenceManager.getDefaultSharedPreferences( context );
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootNotification.java b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootNotification.java
index f6402e51..c2be4e53 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootNotification.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootNotification.java
@@ -33,10 +33,13 @@ public class TootNotification extends TootId {
public long time_created_at;
+ public JSONObject json;
+
public static TootNotification parse( LogCategory log, LinkClickContext accopunt, JSONObject src ){
if( src == null ) return null;
try{
TootNotification dst = new TootNotification();
+ dst.json = src;
dst.id = src.optLong( "id" );
dst.type = Utils.optStringX( src, "type" );
dst.created_at = Utils.optStringX( src, "created_at" );
diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/NotificationTracking.java b/app/src/main/java/jp/juggler/subwaytooter/table/NotificationTracking.java
new file mode 100644
index 00000000..51a6eb31
--- /dev/null
+++ b/app/src/main/java/jp/juggler/subwaytooter/table/NotificationTracking.java
@@ -0,0 +1,154 @@
+package jp.juggler.subwaytooter.table;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import jp.juggler.subwaytooter.App1;
+import jp.juggler.subwaytooter.util.LogCategory;
+
+public class NotificationTracking {
+
+ private static final LogCategory log = new LogCategory( "NotificationTracking" );
+
+ private static final String table = "noti_trac";
+
+ // アカウントDBの行ID。 サーバ側のIDではない
+ private static final String COL_ACCOUNT_DB_ID = "a";
+
+ // サーバから通知を取得した時刻
+ private static final String COL_LAST_LOAD = "ll";
+
+ // サーバから最後に読んだデータ。既読は排除されてるかも
+ private static final String COL_LAST_DATA = "ld";
+
+ // 通知ID。ここまで既読
+ private static final String COL_NID_READ = "nr";
+
+ // 通知ID。もっとも最近取得したもの
+ private static final String COL_NID_SHOW = "ns";
+
+ // 最後に表示した通知のID
+ private static final String COL_POST_ID = "pi";
+ // 最後に表示した通知の作成時刻
+ private static final String COL_POST_TIME = "pt";
+
+ public static void onDBCreate( SQLiteDatabase db ){
+
+ db.execSQL(
+ "create table if not exists " + table
+ + "(_id INTEGER PRIMARY KEY"
+ + ",a integer not null"
+ + ",ll integer default 0"
+ + ",ld text"
+ + ",nr integer default 0"
+ + ",ns integer default 0"
+ + ",pi integer default 0"
+ + ",pt integer default 0"
+ + ")"
+ );
+ db.execSQL(
+ "create unique index if not exists " + table + "_a on " + table + "(a)"
+ );
+ }
+
+ public static void onDBUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ){
+ if( oldVersion < 2 && newVersion >= 2 ){
+ onDBCreate( db );
+ }
+ }
+
+ private long account_db_id;
+ public long last_load;
+ public long nid_read;
+ public long nid_show;
+
+ public long post_id;
+ public long post_time;
+
+ public String last_data;
+
+ private static final String WHERE_AID = COL_ACCOUNT_DB_ID + "=?";
+
+ public static NotificationTracking load( long account_db_id ){
+ NotificationTracking dst = new NotificationTracking();
+ dst.account_db_id = account_db_id;
+ try{
+ Cursor cursor = App1.getDB().query( table, null,WHERE_AID, new String[]{ Long.toString( account_db_id ) }, null, null, null );
+ try{
+ if( cursor.moveToFirst() ){
+ dst.last_load = cursor.getLong( cursor.getColumnIndex( COL_LAST_LOAD ) );
+ dst.nid_read = cursor.getLong( cursor.getColumnIndex( COL_NID_READ ) );
+ dst.nid_show = cursor.getLong( cursor.getColumnIndex( COL_NID_SHOW ) );
+
+ dst.post_id = cursor.getLong( cursor.getColumnIndex( COL_POST_ID ) );
+ dst.post_time = cursor.getLong( cursor.getColumnIndex( COL_POST_TIME ) );
+
+ int idx_last_data = cursor.getColumnIndex( COL_LAST_DATA );
+ dst.last_data = cursor.isNull( idx_last_data ) ? null : cursor.getString( idx_last_data );
+ }
+ }finally{
+ cursor.close();
+ }
+ }catch( Throwable ex ){
+ log.e( ex, "load failed." );
+ }
+ return dst;
+ }
+
+ public void save(){
+ try{
+ ContentValues cv = new ContentValues();
+ cv.put( COL_ACCOUNT_DB_ID, account_db_id );
+ cv.put( COL_LAST_LOAD, last_load );
+ cv.put( COL_NID_READ, nid_read );
+ cv.put( COL_NID_SHOW, nid_show );
+ cv.put( COL_LAST_DATA, last_data );
+ App1.getDB().replace( table, null, cv );
+ }catch( Throwable ex ){
+ log.e( ex, "save failed." );
+ }
+ }
+ public void updatePost(long post_id,long post_time){
+ this.post_id = post_id;
+ this.post_time = post_time;
+ try{
+ ContentValues cv = new ContentValues();
+ cv.put( COL_POST_ID, post_id );
+ cv.put( COL_POST_TIME, post_time );
+ App1.getDB().update( table, cv,WHERE_AID, new String[]{ Long.toString( account_db_id ) } );
+ }catch( Throwable ex ){
+ log.e( ex, "save failed." );
+ }
+ }
+
+ public static void updateRead(long account_db_id){
+ try{
+ String[] where_args = new String[]{ Long.toString( account_db_id ) };
+ Cursor cursor = App1.getDB().query( table, new String[]{ COL_NID_SHOW }, WHERE_AID, where_args, null, null, null );
+ try{
+ if( cursor.moveToFirst() ){
+ long nid = cursor.getLong( cursor.getColumnIndex( COL_NID_SHOW ) );
+ ContentValues cv = new ContentValues();
+ cv.put( COL_NID_READ, nid );
+ App1.getDB().update( table, cv, WHERE_AID,where_args );
+ }
+ }finally{
+ cursor.close();
+ }
+ }catch( Throwable ex ){
+ log.e( ex, "load failed." );
+ }
+ }
+
+ public static void resetPostAll(){
+ try{
+ ContentValues cv = new ContentValues();
+ cv.put( COL_POST_ID, 0 );
+ cv.put( COL_POST_TIME, 0 );
+ App1.getDB().update( table, cv,null,null);
+ }catch( Throwable ex ){
+ log.e( ex, "save failed." );
+ }
+ }
+}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java b/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java
index 6ce92441..56e07974 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java
@@ -29,6 +29,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
private static final String COL_VISIBILITY = "visibility";
private static final String COL_CONFIRM_BOOST = "confirm_boost";
private static final String COL_DONT_HIDE_NSFW = "dont_hide_nsfw";
+ private static final String COL_NOTIFICATION_MENTION = "notification_mention";
+ private static final String COL_NOTIFICATION_BOOST = "notification_boost";
+ private static final String COL_NOTIFICATION_FAVOURITE = "notification_favourite";
+ private static final String COL_NOTIFICATION_FOLLOW = "notification_follow";
public static final long INVALID_ID = -1L;
@@ -40,6 +44,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
public String visibility;
public boolean confirm_boost;
public boolean dont_hide_nsfw;
+ public boolean notification_mention;
+ public boolean notification_boost;
+ public boolean notification_favourite;
+ public boolean notification_follow;
public static void onDBCreate( SQLiteDatabase db ){
db.execSQL(
@@ -52,6 +60,11 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
+ ",visibility text"
+ ",confirm_boost integer default 1"
+ ",dont_hide_nsfw integer default 0"
+ // 以下はDBスキーマ2で追加
+ + ",notification_mention integer default 1"
+ + ",notification_boost integer default 1"
+ + ",notification_favourite integer default 1"
+ + ",notification_follow integer default 1"
+ ")"
);
db.execSQL("create index if not exists " + table + "_user on " + table + "(u)" );
@@ -59,11 +72,31 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
}
public static void onDBUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ){
-
+ if( oldVersion < 2 && newVersion >= 2){
+ try{
+ db.execSQL( "alter table "+table+" add column notification_mention integer default 1" );
+ }catch(Throwable ex){
+ ex.printStackTrace( );
+ }
+ try{
+ db.execSQL( "alter table "+table+" add column notification_boost integer default 1" );
+ }catch(Throwable ex){
+ ex.printStackTrace( );
+ }
+ try{
+ db.execSQL( "alter table "+table+" add column notification_favourite integer default 1" );
+ }catch(Throwable ex){
+ ex.printStackTrace( );
+ }
+ try{
+ db.execSQL( "alter table "+table+" add column notification_follow integer default 1" );
+ }catch(Throwable ex){
+ ex.printStackTrace( );
+ }
+ }
}
private SavedAccount(){
-
}
private static SavedAccount parse( Cursor cursor ) throws JSONException{
@@ -81,6 +114,11 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
dst.confirm_boost = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_CONFIRM_BOOST ) ) );
dst.dont_hide_nsfw = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_DONT_HIDE_NSFW ) ) );
+ dst.notification_mention = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_NOTIFICATION_MENTION ) ) );
+ dst.notification_boost = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_NOTIFICATION_BOOST ) ) );
+ dst.notification_favourite = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_NOTIFICATION_FAVOURITE ) ) );
+ dst.notification_follow = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_NOTIFICATION_FOLLOW ) ) );
+
dst.token_info = new JSONObject( cursor.getString( cursor.getColumnIndex( COL_TOKEN ) ) );
}
return dst;
@@ -125,6 +163,12 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
cv.put( COL_VISIBILITY, visibility );
cv.put( COL_CONFIRM_BOOST, confirm_boost? 1:0 );
cv.put( COL_DONT_HIDE_NSFW, dont_hide_nsfw ? 1: 0 );
+
+ cv.put( COL_NOTIFICATION_MENTION, notification_mention ? 1: 0 );
+ cv.put( COL_NOTIFICATION_BOOST, notification_boost ? 1: 0 );
+ cv.put( COL_NOTIFICATION_FAVOURITE, notification_favourite ? 1: 0 );
+ cv.put( COL_NOTIFICATION_FOLLOW, notification_follow ? 1: 0 );
+
App1.getDB().update( table, cv, COL_ID + "=?", new String[]{ Long.toString(db_id) } );
}
}
@@ -138,6 +182,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
this.confirm_boost = b.confirm_boost;
this.dont_hide_nsfw = b.dont_hide_nsfw;
this.token_info = b.token_info;
+ this.notification_mention = b.notification_follow;
+ this.notification_boost = b.notification_boost;
+ this.notification_favourite = b.notification_favourite;
+ this.notification_follow = b.notification_follow;
}
}
}
diff --git a/app/src/main/res/drawable-hdpi/ic_notification.png b/app/src/main/res/drawable-hdpi/ic_notification.png
new file mode 100644
index 00000000..9b441787
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_notification.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_notification.png b/app/src/main/res/drawable-mdpi/ic_notification.png
new file mode 100644
index 00000000..f0df3a01
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_notification.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_notification.png b/app/src/main/res/drawable-xhdpi/ic_notification.png
new file mode 100644
index 00000000..feeca4c4
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_notification.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification.png b/app/src/main/res/drawable-xxhdpi/ic_notification.png
new file mode 100644
index 00000000..0f81d8c4
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_notification.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_notification.png b/app/src/main/res/drawable-xxxhdpi/ic_notification.png
new file mode 100644
index 00000000..e846b2b2
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_notification.png differ
diff --git a/app/src/main/res/layout/act_account_setting.xml b/app/src/main/res/layout/act_account_setting.xml
index 503a4cf8..66d6876c 100644
--- a/app/src/main/res/layout/act_account_setting.xml
+++ b/app/src/main/res/layout/act_account_setting.xml
@@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
android:fillViewport="true"
android:scrollbarStyle="outsideOverlay"
>
@@ -61,7 +62,7 @@
android:id="@+id/btnOpenBrowser"
style="@style/setting_horizontal_stretch"
android:ellipsize="start"
- android:text="@string/update_access_token"
+ tools:text="open http://mastodon.juggler.jp/"
android:textAllCaps="false"
/>
@@ -118,8 +119,12 @@
+
@@ -135,12 +140,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
index 098c28ad..36c421b5 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
index ec0e8bf8..fc9884c2 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index d5accf2f..ee121158 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 3699cd1a..6ec54534 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index d6af91eb..e801b4dc 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 22b5422f..f6b7b7d7 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -145,4 +145,8 @@
バージョン %1$s
OSSライセンス
https://%1$s/ を開く
+ 返信
+ %1$d件の通知
+ ブースト
+ お気に入り
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 31a20bec..4eb45212 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -144,4 +144,8 @@
Please support this app!
version %1$s
open https://%1$s/
+ boost
+ favourite
+ mention
+ %1$d notifications
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 911a41f3..ff05acdd 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -82,7 +82,10 @@
- wrap_content
- 1
-
+
diff --git a/ic_notification-817.png b/ic_notification-817.png
new file mode 100644
index 00000000..976cdd69
Binary files /dev/null and b/ic_notification-817.png differ
diff --git a/resizeLauncherIcon.pl b/resizeLauncherIcon.pl
index ca1d2ed2..79349d79 100644
--- a/resizeLauncherIcon.pl
+++ b/resizeLauncherIcon.pl
@@ -49,4 +49,4 @@ sub resize_scales{
my $res_dir = "app/src/main/res";
resize_scales( "ic_launcher-1024.png",$res_dir,"mipmap","ic_launcher",0,48);
#resize_scales( "ic_app_logo-512.png",$res_dir,"drawable","ic_app_logo",0,32);
-#resize_scales( "ic_service-512.png",$res_dir,"drawable","ic_service",0,24);
+resize_scales( "ic_notification-817.png",$res_dir,"drawable","ic_notification",0,24);