v0.1.0 プッシュ通知。タブの並び順が変わるバグの修正。
|
@ -9,8 +9,8 @@ android {
|
||||||
applicationId "jp.juggler.subwaytooter"
|
applicationId "jp.juggler.subwaytooter"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 8
|
versionCode 10
|
||||||
versionName "0.0.8"
|
versionName "0.1.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
package="jp.juggler.subwaytooter">
|
package="jp.juggler.subwaytooter">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App1"
|
android:name=".App1"
|
||||||
|
@ -12,6 +14,14 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
>
|
>
|
||||||
|
<receiver android:name=".AlarmReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<service android:name=".AlarmService" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ActMain"
|
android:name=".ActMain"
|
||||||
|
|
|
@ -13,6 +13,7 @@ import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.Switch;
|
import android.widget.Switch;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -29,10 +30,10 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
|
|
||||||
static final String KEY_ACCOUNT_DB_ID = "account_db_id";
|
static final String KEY_ACCOUNT_DB_ID = "account_db_id";
|
||||||
|
|
||||||
public static void open( Activity activity, SavedAccount ai ,int requestCode){
|
public static void open( Activity activity, SavedAccount ai, int requestCode ){
|
||||||
Intent intent = new Intent( activity, ActAccountSetting.class );
|
Intent intent = new Intent( activity, ActAccountSetting.class );
|
||||||
intent.putExtra( KEY_ACCOUNT_DB_ID, ai.db_id );
|
intent.putExtra( KEY_ACCOUNT_DB_ID, ai.db_id );
|
||||||
activity.startActivityForResult( intent ,requestCode);
|
activity.startActivityForResult( intent, requestCode );
|
||||||
}
|
}
|
||||||
|
|
||||||
SavedAccount account;
|
SavedAccount account;
|
||||||
|
@ -45,7 +46,7 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
if( account == null ) finish();
|
if( account == null ) finish();
|
||||||
loadUIFromData( account );
|
loadUIFromData( account );
|
||||||
|
|
||||||
btnOpenBrowser.setText(getString(R.string.open_instance_website,account.host));
|
btnOpenBrowser.setText( getString( R.string.open_instance_website, account.host ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
TextView tvInstance;
|
TextView tvInstance;
|
||||||
|
@ -56,6 +57,10 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
Switch swConfirmBeforeBoost;
|
Switch swConfirmBeforeBoost;
|
||||||
Switch swNSFWOpen;
|
Switch swNSFWOpen;
|
||||||
Button btnOpenBrowser;
|
Button btnOpenBrowser;
|
||||||
|
CheckBox cbNotificationMention;
|
||||||
|
CheckBox cbNotificationBoost;
|
||||||
|
CheckBox cbNotificationFavourite;
|
||||||
|
CheckBox cbNotificationFollow;
|
||||||
|
|
||||||
private void initUI(){
|
private void initUI(){
|
||||||
setContentView( R.layout.act_account_setting );
|
setContentView( R.layout.act_account_setting );
|
||||||
|
@ -67,6 +72,10 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
swConfirmBeforeBoost = (Switch) findViewById( R.id.swConfirmBeforeBoost );
|
swConfirmBeforeBoost = (Switch) findViewById( R.id.swConfirmBeforeBoost );
|
||||||
swNSFWOpen = (Switch) findViewById( R.id.swNSFWOpen );
|
swNSFWOpen = (Switch) findViewById( R.id.swNSFWOpen );
|
||||||
btnOpenBrowser = (Button) findViewById( R.id.btnOpenBrowser );
|
btnOpenBrowser = (Button) findViewById( R.id.btnOpenBrowser );
|
||||||
|
cbNotificationMention = (CheckBox) findViewById( R.id.cbNotificationMention );
|
||||||
|
cbNotificationBoost = (CheckBox) findViewById( R.id.cbNotificationBoost );
|
||||||
|
cbNotificationFavourite = (CheckBox) findViewById( R.id.cbNotificationFavourite );
|
||||||
|
cbNotificationFollow = (CheckBox) findViewById( R.id.cbNotificationFollow );
|
||||||
|
|
||||||
btnOpenBrowser.setOnClickListener( this );
|
btnOpenBrowser.setOnClickListener( this );
|
||||||
btnAccessToken.setOnClickListener( this );
|
btnAccessToken.setOnClickListener( this );
|
||||||
|
@ -75,8 +84,14 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
|
|
||||||
swNSFWOpen.setOnCheckedChangeListener( this );
|
swNSFWOpen.setOnCheckedChangeListener( this );
|
||||||
swConfirmBeforeBoost.setOnCheckedChangeListener( this );
|
swConfirmBeforeBoost.setOnCheckedChangeListener( this );
|
||||||
|
cbNotificationMention.setOnCheckedChangeListener( this );
|
||||||
|
cbNotificationBoost.setOnCheckedChangeListener( this );
|
||||||
|
cbNotificationFavourite.setOnCheckedChangeListener( this );
|
||||||
|
cbNotificationFollow.setOnCheckedChangeListener( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean loading = false;
|
||||||
|
|
||||||
private void loadUIFromData( SavedAccount a ){
|
private void loadUIFromData( SavedAccount a ){
|
||||||
tvInstance.setText( a.host );
|
tvInstance.setText( a.host );
|
||||||
tvUser.setText( a.acct );
|
tvUser.setText( a.acct );
|
||||||
|
@ -85,26 +100,44 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
if( sv != null ){
|
if( sv != null ){
|
||||||
visibility = sv;
|
visibility = sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loading = true;
|
||||||
|
|
||||||
swConfirmBeforeBoost.setChecked( a.confirm_boost );
|
swConfirmBeforeBoost.setChecked( a.confirm_boost );
|
||||||
swNSFWOpen.setChecked( a.dont_hide_nsfw );
|
swNSFWOpen.setChecked( a.dont_hide_nsfw );
|
||||||
|
cbNotificationMention.setChecked( a.notification_mention );
|
||||||
|
cbNotificationBoost.setChecked( a.notification_boost );
|
||||||
|
cbNotificationFavourite.setChecked( a.notification_favourite );
|
||||||
|
cbNotificationFollow.setChecked( a.notification_follow );
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override protected void onStop(){
|
||||||
|
AlarmService.startCheck(this);
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
private void saveUIToData(){
|
private void saveUIToData(){
|
||||||
account.visibility = visibility;
|
account.visibility = visibility;
|
||||||
account.confirm_boost = swConfirmBeforeBoost.isChecked();
|
account.confirm_boost = swConfirmBeforeBoost.isChecked();
|
||||||
account.dont_hide_nsfw = swNSFWOpen.isChecked();
|
account.dont_hide_nsfw = swNSFWOpen.isChecked();
|
||||||
|
account.notification_mention = cbNotificationMention.isChecked();
|
||||||
|
account.notification_boost = cbNotificationBoost.isChecked();
|
||||||
|
account.notification_favourite = cbNotificationFavourite.isChecked();
|
||||||
|
account.notification_follow = cbNotificationFollow.isChecked();
|
||||||
account.saveSetting();
|
account.saveSetting();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override public void onCheckedChanged( CompoundButton buttonView, boolean isChecked ){
|
||||||
public void onCheckedChanged( CompoundButton buttonView, boolean isChecked ){
|
if( loading ) return;
|
||||||
saveUIToData();
|
saveUIToData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override public void onClick( View v ){
|
||||||
public void onClick( View v ){
|
|
||||||
switch( v.getId() ){
|
switch( v.getId() ){
|
||||||
case R.id.btnAccessToken:
|
case R.id.btnAccessToken:
|
||||||
performAccessToken();
|
performAccessToken();
|
||||||
|
@ -116,10 +149,10 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
performVisibility();
|
performVisibility();
|
||||||
break;
|
break;
|
||||||
case R.id.btnOpenBrowser:
|
case R.id.btnOpenBrowser:
|
||||||
open_browser( "https://"+account.host+"/" );
|
open_browser( "https://" + account.host + "/" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void open_browser( String url ){
|
void open_browser( String url ){
|
||||||
try{
|
try{
|
||||||
Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( url ) );
|
Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( url ) );
|
||||||
|
@ -128,7 +161,7 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
|
|
||||||
String visibility = TootStatus.VISIBILITY_PUBLIC;
|
String visibility = TootStatus.VISIBILITY_PUBLIC;
|
||||||
|
@ -197,7 +230,7 @@ public class ActAccountSetting extends AppCompatActivity implements View.OnClick
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
private void performAccessToken(){
|
private void performAccessToken(){
|
||||||
|
|
||||||
final ProgressDialog progress = new ProgressDialog( ActAccountSetting.this );
|
final ProgressDialog progress = new ProgressDialog( ActAccountSetting.this );
|
||||||
|
|
||||||
final AsyncTask< Void, String, TootApiResult > task = new AsyncTask< Void, String, TootApiResult >() {
|
final AsyncTask< Void, String, TootApiResult > task = new AsyncTask< Void, String, TootApiResult >() {
|
||||||
|
|
|
@ -47,8 +47,7 @@ public class ActAppSetting extends AppCompatActivity implements CompoundButton.O
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveUIToData(){
|
private void saveUIToData(){
|
||||||
pref
|
pref.edit()
|
||||||
.edit()
|
|
||||||
.putBoolean( Pref.KEY_BACK_TO_COLUMN_LIST, swBackToColumnList.isChecked() )
|
.putBoolean( Pref.KEY_BACK_TO_COLUMN_LIST, swBackToColumnList.isChecked() )
|
||||||
.putBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, swDontConfirmBeforeCloseColumn.isChecked() )
|
.putBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, swDontConfirmBeforeCloseColumn.isChecked() )
|
||||||
.apply();
|
.apply();
|
||||||
|
|
|
@ -80,29 +80,16 @@ public class ActMain extends AppCompatActivity
|
||||||
|
|
||||||
initUI();
|
initUI();
|
||||||
|
|
||||||
Uri uri = ActOAuthCallback.last_uri.get();
|
AlarmService.startCheck( this );
|
||||||
if( uri != null ){
|
|
||||||
updateAccessToken( uri );
|
|
||||||
}
|
|
||||||
|
|
||||||
loadColumnList();
|
loadColumnList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onNewIntent( Intent intent ){
|
@Override protected void onDestroy(){
|
||||||
super.onNewIntent( intent );
|
|
||||||
Uri uri = ActOAuthCallback.last_uri.get();
|
|
||||||
if( uri != null ){
|
|
||||||
updateAccessToken( uri );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy(){
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override protected void onResume(){
|
||||||
protected void onResume(){
|
|
||||||
super.onResume();
|
super.onResume();
|
||||||
HTMLDecoder.link_callback = link_click_listener;
|
HTMLDecoder.link_callback = link_click_listener;
|
||||||
|
|
||||||
|
@ -122,6 +109,7 @@ public class ActMain extends AppCompatActivity
|
||||||
}
|
}
|
||||||
if( bRemoved ){
|
if( bRemoved ){
|
||||||
pager_adapter.setOrder( pager, new_order );
|
pager_adapter.setOrder( pager, new_order );
|
||||||
|
saveColumnList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,12 +124,16 @@ public class ActMain extends AppCompatActivity
|
||||||
if( pager_adapter.getCount() == 0 ){
|
if( pager_adapter.getCount() == 0 ){
|
||||||
llEmpty.setVisibility( View.VISIBLE );
|
llEmpty.setVisibility( View.VISIBLE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uri uri = ActOAuthCallback.last_uri.get();
|
||||||
|
if( uri != null ){
|
||||||
|
ActOAuthCallback.last_uri.set( null );
|
||||||
|
updateAccessToken( uri );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override protected void onPause(){
|
||||||
protected void onPause(){
|
|
||||||
HTMLDecoder.link_callback = null;
|
HTMLDecoder.link_callback = null;
|
||||||
saveColumnList();
|
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +156,7 @@ public class ActMain extends AppCompatActivity
|
||||||
ArrayList< Integer > order = data.getIntegerArrayListExtra( ActColumnList.EXTRA_ORDER );
|
ArrayList< Integer > order = data.getIntegerArrayListExtra( ActColumnList.EXTRA_ORDER );
|
||||||
if( order != null && isOrderChanged( order ) ){
|
if( order != null && isOrderChanged( order ) ){
|
||||||
pager_adapter.setOrder( pager, order );
|
pager_adapter.setOrder( pager, order );
|
||||||
|
saveColumnList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( pager_adapter.column_list.isEmpty() ){
|
if( pager_adapter.column_list.isEmpty() ){
|
||||||
|
@ -413,6 +406,25 @@ public class ActMain extends AppCompatActivity
|
||||||
|
|
||||||
private void updateAccessToken( final Uri uri ){
|
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 ProgressDialog progress = new ProgressDialog( ActMain.this );
|
||||||
|
|
||||||
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
|
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 );
|
this.row_id = SavedAccount.insert( host, user, result.object, result.token_info );
|
||||||
SavedAccount account = SavedAccount.loadAccount( log, row_id );
|
SavedAccount account = SavedAccount.loadAccount( log, row_id );
|
||||||
if( account != null ){
|
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_showing = pager.getCurrentItem();
|
||||||
int page_delete = pager_adapter.column_list.indexOf( column );
|
int page_delete = pager_adapter.column_list.indexOf( column );
|
||||||
pager_adapter.removeColumn( pager, column );
|
pager_adapter.removeColumn( pager, column );
|
||||||
|
saveColumnList();
|
||||||
if( pager_adapter.getCount() == 0 ){
|
if( pager_adapter.getCount() == 0 ){
|
||||||
llEmpty.setVisibility( View.VISIBLE );
|
llEmpty.setVisibility( View.VISIBLE );
|
||||||
}else if( page_showing > 0 && page_showing == page_delete ){
|
}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 ){
|
for( Column column : pager_adapter.column_list ){
|
||||||
if( column.isSameSpec( ai, type, params ) ){
|
if( column.isSameSpec( ai, type, params ) ){
|
||||||
pager.setCurrentItem( pager_adapter.column_list.indexOf( column ), true );
|
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 );
|
Column col = new Column( ActMain.this, ai, type, params );
|
||||||
int idx = pager_adapter.addColumn( pager, col );
|
int idx = pager_adapter.addColumn( pager, col );
|
||||||
|
saveColumnList();
|
||||||
pager.setCurrentItem( idx, true );
|
pager.setCurrentItem( idx, true );
|
||||||
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAccountUpdated( SavedAccount data ){
|
private void onAccountUpdated( SavedAccount data ){
|
||||||
|
@ -641,7 +657,7 @@ public class ActMain extends AppCompatActivity
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public interface GetAccountCallback {
|
interface GetAccountCallback {
|
||||||
// return account information
|
// return account information
|
||||||
// if failed, account is null.
|
// if failed, account is null.
|
||||||
void onGetAccount( TootAccount account );
|
void onGetAccount( TootAccount account );
|
||||||
|
@ -1100,7 +1116,7 @@ public class ActMain extends AppCompatActivity
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|
||||||
private void performAccountSetting(){
|
private void performAccountSetting(){
|
||||||
AccountPicker.pick( this, true,new AccountPicker.AccountPickerCallback() {
|
AccountPicker.pick( this, true, new AccountPicker.AccountPickerCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onAccountPicked( SavedAccount ai ){
|
public void onAccountPicked( SavedAccount ai ){
|
||||||
ActAccountSetting.open( ActMain.this, ai, REQUEST_CODE_ACCOUNT_SETTING );
|
ActAccountSetting.open( ActMain.this, ai, REQUEST_CODE_ACCOUNT_SETTING );
|
||||||
|
@ -1137,6 +1153,13 @@ public class ActMain extends AppCompatActivity
|
||||||
startActivityForResult( intent, REQUEST_CODE_COLUMN_LIST );
|
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";
|
static final String FILE_COLUMN_LIST = "column_list";
|
||||||
|
|
||||||
private void saveColumnList(){
|
private void saveColumnList(){
|
||||||
|
@ -1170,7 +1193,7 @@ public class ActMain extends AppCompatActivity
|
||||||
try{
|
try{
|
||||||
JSONObject src = array.optJSONObject( i );
|
JSONObject src = array.optJSONObject( i );
|
||||||
Column col = new Column( ActMain.this, src );
|
Column col = new Column( ActMain.this, src );
|
||||||
pager_adapter.addColumn( pager, col );
|
pager_adapter.addColumn( pager, col, pager_adapter.getCount() );
|
||||||
}catch( Throwable ex ){
|
}catch( Throwable ex ){
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -1191,7 +1214,7 @@ public class ActMain extends AppCompatActivity
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public interface RelationChangedCallback {
|
interface RelationChangedCallback {
|
||||||
// void onRelationChanged( TootRelationShip relationship );
|
// void onRelationChanged( TootRelationShip relationship );
|
||||||
void onRelationChanged();
|
void onRelationChanged();
|
||||||
}
|
}
|
||||||
|
@ -1226,8 +1249,8 @@ public class ActMain extends AppCompatActivity
|
||||||
if( result.object != null ){
|
if( result.object != null ){
|
||||||
remote_who = TootAccount.parse( log, access_info, result.object );
|
remote_who = TootAccount.parse( log, access_info, result.object );
|
||||||
|
|
||||||
Utils.showToast( ActMain.this, false, bFollow ? R.string.follow_succeeded : R.string.unfollow_succeeded );
|
Utils.showToast( ActMain.this, false, R.string.follow_succeeded );
|
||||||
}else if( bFollow && who.locked && result.response.code() == 422 ){
|
}else if( who.locked && result.response.code() == 422 ){
|
||||||
Utils.showToast( ActMain.this, false, R.string.cant_follow_locked_user );
|
Utils.showToast( ActMain.this, false, R.string.cant_follow_locked_user );
|
||||||
}else{
|
}else{
|
||||||
Utils.showToast( ActMain.this, false, result.error );
|
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 ){
|
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
|
@Override
|
||||||
public void onAccountPicked( SavedAccount ai ){
|
public void onAccountPicked( SavedAccount ai ){
|
||||||
String acct = who.acct;
|
String acct = who.acct;
|
||||||
|
@ -1502,9 +1525,8 @@ public class ActMain extends AppCompatActivity
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ReportCompleteCallback {
|
interface ReportCompleteCallback {
|
||||||
void onReportComplete( TootApiResult result );
|
void onReportComplete( TootApiResult result );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callReport( final SavedAccount account, final TootAccount who, final TootStatus status
|
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() ){
|
if( ! tmp_list.isEmpty() ){
|
||||||
dialog.addAction( getString( R.string.favourite_from_another_account ), new Runnable() {
|
dialog.addAction( getString( R.string.favourite_from_another_account ), new Runnable() {
|
||||||
@Override public void run(){
|
@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 ){
|
@Override public void onAccountPicked( SavedAccount ai ){
|
||||||
if( ai != null ) performFavourite( ai, status );
|
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() {
|
dialog.addAction( getString( R.string.boost_from_another_account ), new Runnable() {
|
||||||
@Override public void run(){
|
@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 ){
|
@Override public void onAccountPicked( SavedAccount ai ){
|
||||||
if( ai != null ) performBoost( ai, status, false );
|
if( ai != null ) performBoost( ai, status, false );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import jp.juggler.subwaytooter.table.ClientInfo;
|
||||||
import jp.juggler.subwaytooter.table.ContentWarning;
|
import jp.juggler.subwaytooter.table.ContentWarning;
|
||||||
import jp.juggler.subwaytooter.table.LogData;
|
import jp.juggler.subwaytooter.table.LogData;
|
||||||
import jp.juggler.subwaytooter.table.MediaShown;
|
import jp.juggler.subwaytooter.table.MediaShown;
|
||||||
|
import jp.juggler.subwaytooter.table.NotificationTracking;
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
|
import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
|
||||||
|
@ -26,38 +27,11 @@ import uk.co.chrisjenx.calligraphy.TypefaceUtils;
|
||||||
|
|
||||||
public class App1 extends Application {
|
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 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;
|
static DBOpenHelper db_open_helper;
|
||||||
|
|
||||||
|
@ -65,9 +39,10 @@ public class App1 extends Application {
|
||||||
return db_open_helper.getWritableDatabase();
|
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 );
|
super( context, DB_NAME, null, DB_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +54,7 @@ public class App1 extends Application {
|
||||||
ClientInfo.onDBCreate( db );
|
ClientInfo.onDBCreate( db );
|
||||||
MediaShown.onDBCreate(db);
|
MediaShown.onDBCreate(db);
|
||||||
ContentWarning.onDBCreate(db);
|
ContentWarning.onDBCreate(db);
|
||||||
|
NotificationTracking.onDBCreate(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,6 +65,7 @@ public class App1 extends Application {
|
||||||
ClientInfo.onDBUpgrade( db, oldVersion, newVersion );
|
ClientInfo.onDBUpgrade( db, oldVersion, newVersion );
|
||||||
MediaShown.onDBUpgrade( db, oldVersion, newVersion );
|
MediaShown.onDBUpgrade( db, oldVersion, newVersion );
|
||||||
ContentWarning.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;
|
return image_loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MyImageLoader extends ImageLoader {
|
private static class MyImageLoader extends ImageLoader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new 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<String, Bitmap> mCache;
|
private LruCache<String, Bitmap> mCache;
|
||||||
|
|
||||||
|
@ -154,4 +131,38 @@ public class App1 extends Application {
|
||||||
|
|
||||||
// public static final RelationshipMap relationship_map = new RelationshipMap();
|
// 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package jp.juggler.subwaytooter;
|
package jp.juggler.subwaytooter;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
@ -72,9 +73,9 @@ class Column {
|
||||||
static final String KEY_COLUMN_NAME = "column_name";
|
static final String KEY_COLUMN_NAME = "column_name";
|
||||||
static final String KEY_OLD_INDEX = "old_index";
|
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;
|
final int type;
|
||||||
static final int TYPE_HOME = 1;
|
static final int TYPE_HOME = 1;
|
||||||
|
@ -82,7 +83,7 @@ class Column {
|
||||||
static final int TYPE_FEDERATE = 3;
|
static final int TYPE_FEDERATE = 3;
|
||||||
static final int TYPE_PROFILE = 4;
|
static final int TYPE_PROFILE = 4;
|
||||||
static final int TYPE_FAVOURITES = 5;
|
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_NOTIFICATIONS = 7;
|
||||||
static final int TYPE_CONVERSATION = 8;
|
static final int TYPE_CONVERSATION = 8;
|
||||||
static final int TYPE_HASHTAG = 9;
|
static final int TYPE_HASHTAG = 9;
|
||||||
|
@ -106,7 +107,7 @@ class Column {
|
||||||
int scroll_pos;
|
int scroll_pos;
|
||||||
int scroll_y;
|
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.activity = activity;
|
||||||
this.access_info = access_info;
|
this.access_info = access_info;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -155,10 +156,13 @@ class Column {
|
||||||
item.put( KEY_OLD_INDEX, old_index );
|
item.put( KEY_OLD_INDEX, old_index );
|
||||||
}
|
}
|
||||||
|
|
||||||
Column( ActMain activity, JSONObject src ){
|
Column( @NonNull ActMain activity, JSONObject src ){
|
||||||
this.activity = activity;
|
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 );
|
this.type = src.optInt( KEY_TYPE );
|
||||||
switch( type ){
|
switch( type ){
|
||||||
case TYPE_CONVERSATION:
|
case TYPE_CONVERSATION:
|
||||||
|
@ -452,8 +456,14 @@ class Column {
|
||||||
TootApiResult parseNotifications( TootApiResult result ){
|
TootApiResult parseNotifications( TootApiResult result ){
|
||||||
if( result != null ){
|
if( result != null ){
|
||||||
saveRange( result, true, true );
|
saveRange( result, true, true );
|
||||||
list_tmp = new ArrayList<>();
|
TootNotification.List src= TootNotification.parseList( log, access_info, result.array );
|
||||||
list_tmp.addAll( 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -594,6 +604,8 @@ class Column {
|
||||||
if( list_tmp != null ){
|
if( list_tmp != null ){
|
||||||
list_data.clear();
|
list_data.clear();
|
||||||
list_data.addAll( list_tmp );
|
list_data.addAll( list_tmp );
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -700,8 +712,14 @@ class Column {
|
||||||
TootApiResult parseNotifications( TootApiResult result ){
|
TootApiResult parseNotifications( TootApiResult result ){
|
||||||
if( result != null ){
|
if( result != null ){
|
||||||
saveRange( result, bBottom, ! bBottom );
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ import android.preference.PreferenceManager;
|
||||||
|
|
||||||
public class Pref {
|
public class Pref {
|
||||||
|
|
||||||
public static SharedPreferences pref(Context context){
|
|
||||||
|
public static SharedPreferences pref( Context context){
|
||||||
return PreferenceManager.getDefaultSharedPreferences( context );
|
return PreferenceManager.getDefaultSharedPreferences( context );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,13 @@ public class TootNotification extends TootId {
|
||||||
|
|
||||||
public long time_created_at;
|
public long time_created_at;
|
||||||
|
|
||||||
|
public JSONObject json;
|
||||||
|
|
||||||
public static TootNotification parse( LogCategory log, LinkClickContext accopunt, JSONObject src ){
|
public static TootNotification parse( LogCategory log, LinkClickContext accopunt, JSONObject src ){
|
||||||
if( src == null ) return null;
|
if( src == null ) return null;
|
||||||
try{
|
try{
|
||||||
TootNotification dst = new TootNotification();
|
TootNotification dst = new TootNotification();
|
||||||
|
dst.json = src;
|
||||||
dst.id = src.optLong( "id" );
|
dst.id = src.optLong( "id" );
|
||||||
dst.type = Utils.optStringX( src, "type" );
|
dst.type = Utils.optStringX( src, "type" );
|
||||||
dst.created_at = Utils.optStringX( src, "created_at" );
|
dst.created_at = Utils.optStringX( src, "created_at" );
|
||||||
|
|
|
@ -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." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
|
||||||
private static final String COL_VISIBILITY = "visibility";
|
private static final String COL_VISIBILITY = "visibility";
|
||||||
private static final String COL_CONFIRM_BOOST = "confirm_boost";
|
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_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;
|
public static final long INVALID_ID = -1L;
|
||||||
|
|
||||||
|
@ -40,6 +44,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
|
||||||
public String visibility;
|
public String visibility;
|
||||||
public boolean confirm_boost;
|
public boolean confirm_boost;
|
||||||
public boolean dont_hide_nsfw;
|
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 ){
|
public static void onDBCreate( SQLiteDatabase db ){
|
||||||
db.execSQL(
|
db.execSQL(
|
||||||
|
@ -52,6 +60,11 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
|
||||||
+ ",visibility text"
|
+ ",visibility text"
|
||||||
+ ",confirm_boost integer default 1"
|
+ ",confirm_boost integer default 1"
|
||||||
+ ",dont_hide_nsfw integer default 0"
|
+ ",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)" );
|
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 ){
|
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 SavedAccount(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SavedAccount parse( Cursor cursor ) throws JSONException{
|
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.confirm_boost = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_CONFIRM_BOOST ) ) );
|
||||||
dst.dont_hide_nsfw = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_DONT_HIDE_NSFW ) ) );
|
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 ) ) );
|
dst.token_info = new JSONObject( cursor.getString( cursor.getColumnIndex( COL_TOKEN ) ) );
|
||||||
}
|
}
|
||||||
return dst;
|
return dst;
|
||||||
|
@ -125,6 +163,12 @@ public class SavedAccount extends TootAccount implements LinkClickContext{
|
||||||
cv.put( COL_VISIBILITY, visibility );
|
cv.put( COL_VISIBILITY, visibility );
|
||||||
cv.put( COL_CONFIRM_BOOST, confirm_boost? 1:0 );
|
cv.put( COL_CONFIRM_BOOST, confirm_boost? 1:0 );
|
||||||
cv.put( COL_DONT_HIDE_NSFW, dont_hide_nsfw ? 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) } );
|
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.confirm_boost = b.confirm_boost;
|
||||||
this.dont_hide_nsfw = b.dont_hide_nsfw;
|
this.dont_hide_nsfw = b.dont_hide_nsfw;
|
||||||
this.token_info = b.token_info;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 642 B |
After Width: | Height: | Size: 484 B |
After Width: | Height: | Size: 792 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
|
@ -3,6 +3,7 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:fillViewport="true"
|
android:fillViewport="true"
|
||||||
android:scrollbarStyle="outsideOverlay"
|
android:scrollbarStyle="outsideOverlay"
|
||||||
>
|
>
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
android:id="@+id/btnOpenBrowser"
|
android:id="@+id/btnOpenBrowser"
|
||||||
style="@style/setting_horizontal_stretch"
|
style="@style/setting_horizontal_stretch"
|
||||||
android:ellipsize="start"
|
android:ellipsize="start"
|
||||||
android:text="@string/update_access_token"
|
tools:text="open http://mastodon.juggler.jp/"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -118,8 +119,12 @@
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:id="@+id/swConfirmBeforeBoost"
|
android:id="@+id/swConfirmBeforeBoost"
|
||||||
style="@style/setting_horizontal_stretch"
|
style="@style/setting_wrap"
|
||||||
android:gravity="center"
|
/>
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -135,12 +140,56 @@
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:id="@+id/swNSFWOpen"
|
android:id="@+id/swNSFWOpen"
|
||||||
style="@style/setting_horizontal_stretch"
|
style="@style/setting_wrap"
|
||||||
android:gravity="center"
|
/>
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/setting_row_label"
|
||||||
|
android:text="@string/notifications"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cbNotificationMention"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:text="@string/mention2"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cbNotificationBoost"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:text="@string/boost"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cbNotificationFavourite"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:text="@string/favourite"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cbNotificationFollow"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:text="@string/follow"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!--<View style="@style/setting_divider"/>-->
|
<!--<View style="@style/setting_divider"/>-->
|
||||||
|
|
||||||
<!--<TextView-->
|
<!--<TextView-->
|
||||||
|
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
@ -145,4 +145,8 @@
|
||||||
<string name="version_is">バージョン %1$s</string>
|
<string name="version_is">バージョン %1$s</string>
|
||||||
<string name="oss_license">OSSライセンス</string>
|
<string name="oss_license">OSSライセンス</string>
|
||||||
<string name="open_instance_website">https://%1$s/ を開く</string>
|
<string name="open_instance_website">https://%1$s/ を開く</string>
|
||||||
|
<string name="mention2">返信</string>
|
||||||
|
<string name="notification_count">%1$d件の通知</string>
|
||||||
|
<string name="boost">ブースト</string>
|
||||||
|
<string name="favourite">お気に入り</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -144,4 +144,8 @@
|
||||||
<string name="please_donate">Please support this app!</string>
|
<string name="please_donate">Please support this app!</string>
|
||||||
<string name="version_is">version %1$s</string>
|
<string name="version_is">version %1$s</string>
|
||||||
<string name="open_instance_website">open https://%1$s/</string>
|
<string name="open_instance_website">open https://%1$s/</string>
|
||||||
|
<string name="boost">boost</string>
|
||||||
|
<string name="favourite">favourite</string>
|
||||||
|
<string name="mention2">mention</string>
|
||||||
|
<string name="notification_count">%1$d notifications</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -82,7 +82,10 @@
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_weight">1</item>
|
<item name="android:layout_weight">1</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="setting_wrap">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
</style>
|
||||||
<style name="setting_edit_text" parent="@style/setting_horizontal_stretch">
|
<style name="setting_edit_text" parent="@style/setting_horizontal_stretch">
|
||||||
<item name="android:imeOptions">actionDone</item>
|
<item name="android:imeOptions">actionDone</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
After Width: | Height: | Size: 13 KiB |
|
@ -49,4 +49,4 @@ sub resize_scales{
|
||||||
my $res_dir = "app/src/main/res";
|
my $res_dir = "app/src/main/res";
|
||||||
resize_scales( "ic_launcher-1024.png",$res_dir,"mipmap","ic_launcher",0,48);
|
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_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);
|
||||||
|
|