アプリ設定にアプリデータのエクスポートとインポートを追加
This commit is contained in:
parent
0c7b634fd7
commit
7030d30a92
|
@ -161,6 +161,17 @@
|
|||
<activity android:name="com.yasesprox.android.transcommusdk.CreateAccountActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize"></activity>
|
||||
<activity android:name="com.yasesprox.android.transcommusdk.TranslateActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize"></activity>
|
||||
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="jp.juggler.subwaytooter.FileProvider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_provider_path" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,17 +1,23 @@
|
|||
package jp.juggler.subwaytooter;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.JsonWriter;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
@ -27,11 +33,13 @@ import com.jrummyapps.android.colorpicker.ColorPickerDialog;
|
|||
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.FileWriterWithEncoding;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
public class ActAppSetting extends AppCompatActivity
|
||||
|
@ -40,6 +48,7 @@ public class ActAppSetting extends AppCompatActivity
|
|||
, View.OnClickListener
|
||||
, ColorPickerDialogListener, TextWatcher
|
||||
{
|
||||
static final LogCategory log = new LogCategory( "ActAppSetting" );
|
||||
|
||||
public static void open( ActMain activity, int request_code ){
|
||||
activity.startActivityForResult( new Intent( activity, ActAppSetting.class ), request_code );
|
||||
|
@ -103,9 +112,7 @@ public class ActAppSetting extends AppCompatActivity
|
|||
private void initUI(){
|
||||
setContentView( R.layout.act_app_setting );
|
||||
|
||||
|
||||
Styler.fixHorizontalPadding(findViewById( R.id.svContent ));
|
||||
|
||||
Styler.fixHorizontalPadding( findViewById( R.id.svContent ) );
|
||||
|
||||
swDontConfirmBeforeCloseColumn = (Switch) findViewById( R.id.swDontConfirmBeforeCloseColumn );
|
||||
swDontConfirmBeforeCloseColumn.setOnCheckedChangeListener( this );
|
||||
|
@ -213,6 +220,8 @@ public class ActAppSetting extends AppCompatActivity
|
|||
findViewById( R.id.btnTabDividerColorReset ).setOnClickListener( this );
|
||||
findViewById( R.id.btnTimelineFontEdit ).setOnClickListener( this );
|
||||
findViewById( R.id.btnTimelineFontReset ).setOnClickListener( this );
|
||||
findViewById( R.id.btnSettingExport ).setOnClickListener( this );
|
||||
findViewById( R.id.btnSettingImport ).setOnClickListener( this );
|
||||
|
||||
ivFooterToot = (ImageView) findViewById( R.id.ivFooterToot );
|
||||
ivFooterMenu = (ImageView) findViewById( R.id.ivFooterMenu );
|
||||
|
@ -223,7 +232,7 @@ public class ActAppSetting extends AppCompatActivity
|
|||
etColumnWidth = (EditText) findViewById( R.id.etColumnWidth );
|
||||
etMediaThumbHeight = (EditText) findViewById( R.id.etMediaThumbHeight );
|
||||
|
||||
tvTimelineFontUrl= (TextView) findViewById( R.id.tvTimelineFontUrl );
|
||||
tvTimelineFontUrl = (TextView) findViewById( R.id.tvTimelineFontUrl );
|
||||
|
||||
etColumnWidth.addTextChangedListener( this );
|
||||
etMediaThumbHeight.addTextChangedListener( this );
|
||||
|
@ -264,16 +273,14 @@ public class ActAppSetting extends AppCompatActivity
|
|||
etColumnWidth.setText( pref.getString( Pref.KEY_COLUMN_WIDTH, "" ) );
|
||||
etMediaThumbHeight.setText( pref.getString( Pref.KEY_MEDIA_THUMB_HEIGHT, "" ) );
|
||||
|
||||
|
||||
timeline_font = pref.getString( Pref.KEY_TIMELINE_FONT, "" );
|
||||
|
||||
timeline_font = pref.getString( Pref.KEY_TIMELINE_FONT, "" );
|
||||
|
||||
load_busy = false;
|
||||
|
||||
showFooterColor();
|
||||
showTimelineFont();
|
||||
}
|
||||
|
||||
|
||||
private void saveUIToData(){
|
||||
if( load_busy ) return;
|
||||
pref.edit()
|
||||
|
@ -383,29 +390,46 @@ public class ActAppSetting extends AppCompatActivity
|
|||
intent.addCategory( Intent.CATEGORY_OPENABLE );
|
||||
intent.setType( "*/*" );
|
||||
startActivityForResult( intent, REQUEST_CODE_TIMELINE_FONT );
|
||||
}catch(Throwable ex){
|
||||
Utils.showToast( this,ex,"could not open picker for font/*" );
|
||||
}catch( Throwable ex ){
|
||||
Utils.showToast( this, ex, "could not open picker for font/*" );
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.btnSettingExport:
|
||||
exportAppData();
|
||||
break;
|
||||
|
||||
case R.id.btnSettingImport:
|
||||
importAppData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static final int REQUEST_CODE_TIMELINE_FONT = 1;
|
||||
static final int REQUEST_CODE_APP_DATA_EXPORT = 2;
|
||||
static final int REQUEST_CODE_APP_DATA_IMPORT = 3;
|
||||
|
||||
@Override protected void onActivityResult( int requestCode, int resultCode, Intent data ){
|
||||
if( resultCode == RESULT_OK && requestCode == REQUEST_CODE_TIMELINE_FONT){
|
||||
if( resultCode == RESULT_OK && requestCode == REQUEST_CODE_TIMELINE_FONT ){
|
||||
if( data != null ){
|
||||
Uri uri = data.getData();
|
||||
if( uri != null ){
|
||||
getContentResolver().takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION );
|
||||
saveTimelineFont( uri);
|
||||
saveTimelineFont( uri );
|
||||
}
|
||||
}
|
||||
}else if( resultCode == RESULT_OK && requestCode == REQUEST_CODE_APP_DATA_IMPORT ){
|
||||
if( data != null ){
|
||||
Uri uri = data.getData();
|
||||
if( uri != null ){
|
||||
getContentResolver().takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION );
|
||||
importAppData( false, uri );
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onActivityResult( requestCode, resultCode, data );
|
||||
}
|
||||
|
||||
|
||||
void openColorPicker( int id, int color ){
|
||||
ColorPickerDialog.Builder builder = ColorPickerDialog.newBuilder()
|
||||
.setDialogType( ColorPickerDialog.TYPE_CUSTOM )
|
||||
|
@ -501,19 +525,18 @@ public class ActAppSetting extends AppCompatActivity
|
|||
saveUIToData();
|
||||
}
|
||||
|
||||
|
||||
private void showTimelineFont(){
|
||||
try{
|
||||
if( ! TextUtils.isEmpty( timeline_font ) ){
|
||||
|
||||
tvTimelineFontUrl.setTypeface( Typeface.DEFAULT );
|
||||
Typeface face = Typeface.createFromFile( timeline_font );
|
||||
tvTimelineFontUrl.setTypeface(face );
|
||||
tvTimelineFontUrl.setTypeface( face );
|
||||
tvTimelineFontUrl.setText( timeline_font );
|
||||
return;
|
||||
}
|
||||
}catch(Throwable ex){
|
||||
ex.printStackTrace( );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
// fallback
|
||||
tvTimelineFontUrl.setText( getString( R.string.not_selected ) );
|
||||
|
@ -529,18 +552,17 @@ public class ActAppSetting extends AppCompatActivity
|
|||
//noinspection ResultOfMethodCallIgnored
|
||||
dir.mkdir();
|
||||
|
||||
File tmp_file = new File( dir, TIMELINE_FONT_FILE_NAME +".tmp" );
|
||||
|
||||
File tmp_file = new File( dir, TIMELINE_FONT_FILE_NAME + ".tmp" );
|
||||
|
||||
InputStream is = getContentResolver().openInputStream( uri );
|
||||
if( is == null ){
|
||||
Utils.showToast( this, false, "openInputStream returns null.");
|
||||
Utils.showToast( this, false, "openInputStream returns null." );
|
||||
return;
|
||||
}
|
||||
try{
|
||||
FileOutputStream os = new FileOutputStream( tmp_file );
|
||||
try{
|
||||
IOUtils.copy( is,os );
|
||||
IOUtils.copy( is, os );
|
||||
}finally{
|
||||
IOUtils.closeQuietly( os );
|
||||
}
|
||||
|
@ -549,16 +571,15 @@ public class ActAppSetting extends AppCompatActivity
|
|||
IOUtils.closeQuietly( is );
|
||||
}
|
||||
|
||||
|
||||
Typeface face = Typeface.createFromFile( tmp_file );
|
||||
if( face == null ){
|
||||
Utils.showToast( this, false, "Typeface.createFromFile() failed.");
|
||||
Utils.showToast( this, false, "Typeface.createFromFile() failed." );
|
||||
return;
|
||||
}
|
||||
|
||||
File file = new File( dir, TIMELINE_FONT_FILE_NAME );
|
||||
if(!tmp_file.renameTo( file ) ){
|
||||
Utils.showToast( this, false, "File operation failed.");
|
||||
if( ! tmp_file.renameTo( file ) ){
|
||||
Utils.showToast( this, false, "File operation failed." );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -566,10 +587,113 @@ public class ActAppSetting extends AppCompatActivity
|
|||
saveUIToData();
|
||||
showTimelineFont();
|
||||
|
||||
}catch(Throwable ex){
|
||||
ex.printStackTrace( );
|
||||
Utils.showToast( this,ex,"saveTimelineFont failed.");
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
Utils.showToast( this, ex, "saveTimelineFont failed." );
|
||||
}
|
||||
}
|
||||
|
||||
private void exportAppData(){
|
||||
final ProgressDialog progress = new ProgressDialog( this );
|
||||
|
||||
final AsyncTask< Void, String, File > task = new AsyncTask< Void, String, File >() {
|
||||
|
||||
@Override protected File doInBackground( Void... params ){
|
||||
try{
|
||||
File cache_dir = getCacheDir();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
cache_dir.mkdir();
|
||||
|
||||
File file = new File( cache_dir, "SubwayTooter." + android.os.Process.myPid() + "." + android.os.Process.myTid() + ".json" );
|
||||
FileWriterWithEncoding w = new FileWriterWithEncoding( file, "UTF-8" );
|
||||
try{
|
||||
JsonWriter jw = new JsonWriter( w );
|
||||
AppDataExporter.encodeAppData( ActAppSetting.this, jw );
|
||||
jw.flush();
|
||||
}finally{
|
||||
IOUtils.closeQuietly( w );
|
||||
}
|
||||
return file;
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
Utils.showToast( ActAppSetting.this, ex, "exportAppData failed." );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override protected void onCancelled( File result ){
|
||||
super.onCancelled( result );
|
||||
}
|
||||
|
||||
@Override protected void onPostExecute( File result ){
|
||||
progress.dismiss();
|
||||
|
||||
if( isCancelled() || result == null ){
|
||||
// cancelled.
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
Uri uri = FileProvider.getUriForFile( ActAppSetting.this, "jp.juggler.subwaytooter.FileProvider", result );
|
||||
Intent intent = new Intent( Intent.ACTION_SEND );
|
||||
intent.setType( getContentResolver().getType( uri ) );
|
||||
intent.putExtra( Intent.EXTRA_SUBJECT, "SubwayTooter app data" );
|
||||
intent.putExtra( Intent.EXTRA_STREAM, uri );
|
||||
|
||||
intent.addFlags( Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION );
|
||||
startActivityForResult( intent, REQUEST_CODE_APP_DATA_EXPORT );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
Utils.showToast( ActAppSetting.this, ex, "exportAppData failed." );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
progress.setIndeterminate( true );
|
||||
progress.setCancelable( true );
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override public void onCancel( DialogInterface dialog ){
|
||||
task.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.show();
|
||||
task.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
private void importAppData(){
|
||||
try{
|
||||
Intent intent = new Intent( Intent.ACTION_OPEN_DOCUMENT );
|
||||
intent.addCategory( Intent.CATEGORY_OPENABLE );
|
||||
intent.setType( "*/*" );
|
||||
startActivityForResult( intent, REQUEST_CODE_APP_DATA_IMPORT );
|
||||
}catch( Throwable ex ){
|
||||
Utils.showToast( this, ex, "importAppData(1) failed." );
|
||||
}
|
||||
}
|
||||
|
||||
private void importAppData( boolean bConfirm, final Uri uri ){
|
||||
|
||||
String type = getContentResolver().getType( uri );
|
||||
log.d( "importAppData type=%s", type );
|
||||
|
||||
if( ! bConfirm ){
|
||||
new AlertDialog.Builder( this )
|
||||
.setMessage( getString( R.string.app_data_import_confirm ) )
|
||||
.setNegativeButton( R.string.cancel, null )
|
||||
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override public void onClick( DialogInterface dialog, int which ){
|
||||
importAppData( true, uri );
|
||||
}
|
||||
} )
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent data = new Intent();
|
||||
data.setData( uri );
|
||||
setResult( ActMain.RESULT_APP_DATA_IMPORT, data );
|
||||
finish();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.support.v7.widget.LinearLayoutManager;
|
|||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.JsonReader;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.support.design.widget.NavigationView;
|
||||
|
@ -40,9 +41,16 @@ import android.widget.ImageButton;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -306,6 +314,10 @@ public class ActMain extends AppCompatActivity
|
|||
return false;
|
||||
}
|
||||
|
||||
// リザルト
|
||||
static final int RESULT_APP_DATA_IMPORT = RESULT_FIRST_USER ;
|
||||
|
||||
// リクエスト
|
||||
static final int REQUEST_CODE_COLUMN_LIST = 1;
|
||||
static final int REQUEST_CODE_ACCOUNT_SETTING = 2;
|
||||
static final int REQUEST_APP_ABOUT = 3;
|
||||
|
@ -383,11 +395,19 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
if( requestCode == REQUEST_APP_SETTING ){
|
||||
showFooterColor();
|
||||
|
||||
if( resultCode == RESULT_APP_DATA_IMPORT ){
|
||||
if( data != null ){
|
||||
importAppData( data.getData() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
super.onActivityResult( requestCode, resultCode, data );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBackPressed(){
|
||||
|
||||
|
@ -3211,4 +3231,153 @@ public class ActMain extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
private void importAppData( final Uri uri ){
|
||||
// remove all columns
|
||||
{
|
||||
if( pager_adapter != null ){
|
||||
pager.setAdapter( null );
|
||||
}
|
||||
for( Column c : app_state.column_list ){
|
||||
c.dispose();
|
||||
}
|
||||
app_state.column_list.clear();
|
||||
if( pager_adapter != null ){
|
||||
pager.setAdapter( pager_adapter );
|
||||
}else{
|
||||
tablet_pager_adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
final ProgressDialog progress = new ProgressDialog( this );
|
||||
|
||||
final AsyncTask< Void, String, ArrayList<Column> > task = new AsyncTask< Void, String, ArrayList<Column> >() {
|
||||
|
||||
void setProgressMessage(final String sv){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override public void run(){
|
||||
progress.setMessage(sv);
|
||||
}
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
@Override protected ArrayList<Column> doInBackground( Void... params ){
|
||||
try{
|
||||
setProgressMessage( "import data to local storage..." );
|
||||
|
||||
File cache_dir = getCacheDir();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
cache_dir.mkdir();
|
||||
File file = new File( cache_dir, "SubwayTooter." + android.os.Process.myPid() + "." + android.os.Process.myTid() + ".json" );
|
||||
|
||||
// ローカルファイルにコピーする
|
||||
InputStream is = getContentResolver().openInputStream( uri );
|
||||
if( is == null ){
|
||||
Utils.showToast( ActMain.this, true,"openInputStream failed.");
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
FileOutputStream os = new FileOutputStream( file );
|
||||
try{
|
||||
IOUtils.copy( is,os );
|
||||
}finally{
|
||||
IOUtils.closeQuietly( os );
|
||||
|
||||
}
|
||||
}finally{
|
||||
IOUtils.closeQuietly( is );
|
||||
}
|
||||
|
||||
|
||||
// 通知サービスを止める
|
||||
setProgressMessage( "reset Notification..." );
|
||||
{
|
||||
AlarmService.mBusyAppDataImportBefore.set(true);
|
||||
AlarmService.mBusyAppDataImportAfter.set(true);
|
||||
|
||||
Intent intent = new Intent(ActMain.this,AlarmService.class);
|
||||
intent.setAction( AlarmService.ACTION_APP_DATA_IMPORT_BEFORE );
|
||||
startService( intent );
|
||||
while( AlarmService.mBusyAppDataImportBefore.get() ){
|
||||
Thread.sleep( 100L );
|
||||
}
|
||||
}
|
||||
|
||||
// JSONを読みだす
|
||||
setProgressMessage( "reading app data..." );
|
||||
Reader r =new InputStreamReader(new FileInputStream(file),"UTF-8");
|
||||
try{
|
||||
JsonReader reader = new JsonReader( r );
|
||||
return AppDataExporter.decodeAppData( ActMain.this, reader );
|
||||
}finally{
|
||||
IOUtils.closeQuietly( r );
|
||||
}
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
Utils.showToast( ActMain.this, ex, "importAppData failed." );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override protected void onCancelled( ArrayList<Column> result ){
|
||||
super.onCancelled( result );
|
||||
}
|
||||
|
||||
@Override protected void onPostExecute( ArrayList<Column> result ){
|
||||
progress.dismiss();
|
||||
|
||||
try{
|
||||
getWindow().clearFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );
|
||||
}catch(Throwable ignored){
|
||||
}
|
||||
|
||||
if( isCancelled() || result == null ){
|
||||
// cancelled.
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
if( pager_adapter != null ){
|
||||
pager.setAdapter( null );
|
||||
}
|
||||
app_state.column_list.clear();
|
||||
app_state.column_list.addAll(result );
|
||||
app_state.saveColumnList();
|
||||
|
||||
if( pager_adapter != null ){
|
||||
pager.setAdapter( pager_adapter );
|
||||
}else{
|
||||
tablet_pager_adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// 通知サービスをリスタート
|
||||
{
|
||||
Intent intent = new Intent(ActMain.this,AlarmService.class);
|
||||
intent.setAction( AlarmService.ACTION_APP_DATA_IMPORT_AFTER );
|
||||
startService( intent );
|
||||
}
|
||||
|
||||
updateColumnStrip();
|
||||
}
|
||||
};
|
||||
|
||||
try{
|
||||
getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );
|
||||
}catch(Throwable ignored){
|
||||
}
|
||||
|
||||
progress.setIndeterminate( true );
|
||||
progress.setCancelable( false );
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override public void onCancel( DialogInterface dialog ){
|
||||
task.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.show();
|
||||
task.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,11 @@ public class AlarmService extends IntentService {
|
|||
private static final String EXTRA_DB_ID = "db_id";
|
||||
private static final String ACTION_DATA_DELETED = "data_deleted";
|
||||
|
||||
public static final String ACTION_APP_DATA_IMPORT_BEFORE = "app_data_import_before";
|
||||
public static final AtomicBoolean mBusyAppDataImportBefore = new AtomicBoolean( false );
|
||||
public static final AtomicBoolean mBusyAppDataImportAfter = new AtomicBoolean( false );
|
||||
public static final String ACTION_APP_DATA_IMPORT_AFTER = "app_data_import_after";
|
||||
|
||||
public AlarmService(){
|
||||
// name: Used to name the worker thread, important only for debugging.
|
||||
super( "AlarmService" );
|
||||
|
@ -98,11 +103,34 @@ public class AlarmService extends IntentService {
|
|||
// 同期処理を行って良い
|
||||
@Override protected void onHandleIntent( @Nullable Intent intent ){
|
||||
|
||||
if( intent != null ){
|
||||
String action = intent.getAction();
|
||||
log.d( "onHandleIntent action=%s", action );
|
||||
|
||||
if( ACTION_APP_DATA_IMPORT_BEFORE.equals( action ) ){
|
||||
alarm_manager.cancel( pi_next );
|
||||
for( SavedAccount a : SavedAccount.loadAccountList( log ) ){
|
||||
try{
|
||||
String notification_tag = Long.toString( a.db_id );
|
||||
notification_manager.cancel( notification_tag, NOTIFICATION_ID );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
mBusyAppDataImportBefore.set( false );
|
||||
return;
|
||||
}else if( ACTION_APP_DATA_IMPORT_AFTER.equals( action ) ){
|
||||
mBusyAppDataImportAfter.set( false );
|
||||
NotificationTracking.resetPostAll();
|
||||
}
|
||||
}
|
||||
|
||||
if( mBusyAppDataImportAfter.get() ) return;
|
||||
|
||||
ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( log );
|
||||
|
||||
if( intent != null ){
|
||||
String action = intent.getAction();
|
||||
log.d( "onHandleIntent action=%s", action );
|
||||
|
||||
if( ACTION_DATA_DELETED.equals( action ) ){
|
||||
deleteCacheData( intent.getLongExtra( EXTRA_DB_ID, - 1L ) );
|
||||
|
@ -122,7 +150,7 @@ public class AlarmService extends IntentService {
|
|||
|
||||
}else if( Intent.ACTION_MY_PACKAGE_REPLACED.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 );
|
||||
|
@ -147,8 +175,6 @@ public class AlarmService extends IntentService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
final AtomicBoolean bAlarmRequired = new AtomicBoolean( false );
|
||||
final HashSet< String > muted_app = MutedApp.getNameSet();
|
||||
final HashSet< String > muted_word = MutedWord.getNameSet();
|
||||
|
|
|
@ -0,0 +1,407 @@
|
|||
package jp.juggler.subwaytooter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonToken;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import jp.juggler.subwaytooter.table.AcctColor;
|
||||
import jp.juggler.subwaytooter.table.MutedApp;
|
||||
import jp.juggler.subwaytooter.table.MutedWord;
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
|
||||
public class AppDataExporter {
|
||||
|
||||
static final LogCategory log = new LogCategory( "AppDataExporter" );
|
||||
|
||||
private static void writeJSONObject( @NonNull JsonWriter writer, @NonNull JSONObject src ) throws IOException, JSONException{
|
||||
writer.beginObject();
|
||||
for( Iterator< String > it = src.keys() ; it.hasNext() ; ){
|
||||
String k = it.next();
|
||||
if( src.isNull( k ) ){
|
||||
writer.name( k );
|
||||
writer.nullValue();
|
||||
}else{
|
||||
Object o = src.get( k );
|
||||
|
||||
if( o instanceof String ){
|
||||
writer.name( k );
|
||||
writer.value( (String) o );
|
||||
|
||||
}else if( o instanceof Boolean ){
|
||||
writer.name( k );
|
||||
writer.value( (Boolean) o );
|
||||
|
||||
}else if( o instanceof Number ){
|
||||
writer.name( k );
|
||||
writer.value( (Number) o );
|
||||
|
||||
}else{
|
||||
throw new RuntimeException( String.format( Locale.JAPAN, "bad data type: JSONObject key =%s", k ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
private static JSONObject readJsonObject( JsonReader reader ) throws IOException, JSONException{
|
||||
JSONObject dst = new JSONObject();
|
||||
|
||||
reader.beginObject();
|
||||
while( reader.hasNext() ){
|
||||
String name = reader.nextName();
|
||||
JsonToken token = reader.peek();
|
||||
switch( token ){
|
||||
|
||||
case NULL:
|
||||
reader.nextNull();
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
dst.put( name, reader.nextString() );
|
||||
break;
|
||||
|
||||
case BOOLEAN:
|
||||
dst.put( name, reader.nextBoolean() );
|
||||
break;
|
||||
|
||||
case NUMBER:
|
||||
dst.put( name, reader.nextDouble() );
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException( String.format( Locale.JAPAN, "bad data type: %s key =%s", token, name ) );
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static void writeFromTable( @NonNull JsonWriter writer, String json_key, String table ) throws IOException{
|
||||
|
||||
writer.name( json_key );
|
||||
writer.beginArray();
|
||||
|
||||
Cursor cursor = App1.getDB().query( table, null, null, null, null, null, null );
|
||||
try{
|
||||
ArrayList< String > names = new ArrayList<>();
|
||||
int column_count = cursor.getColumnCount();
|
||||
for( int i = 0 ; i < column_count ; ++ i ){
|
||||
names.add( cursor.getColumnName( i ) );
|
||||
}
|
||||
while( cursor.moveToNext() ){
|
||||
writer.beginObject();
|
||||
|
||||
for( int i = 0 ; i < column_count ; ++ i ){
|
||||
switch( cursor.getType( i ) ){
|
||||
case Cursor.FIELD_TYPE_NULL:
|
||||
writer.name( names.get( i ) );
|
||||
writer.nullValue();
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_INTEGER:
|
||||
writer.name( names.get( i ) );
|
||||
writer.value( cursor.getLong( i ) );
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_STRING:
|
||||
writer.name( names.get( i ) );
|
||||
writer.value( cursor.getString( i ) );
|
||||
break;
|
||||
|
||||
case Cursor.FIELD_TYPE_FLOAT:
|
||||
Double d = cursor.getDouble( i );
|
||||
if( Double.isNaN( d ) || Double.isInfinite( d ) ){
|
||||
log.w( "column %s is nan or infinite value.", names.get( i ) );
|
||||
}else{
|
||||
writer.name( names.get( i ) );
|
||||
writer.value( d );
|
||||
}
|
||||
break;
|
||||
case Cursor.FIELD_TYPE_BLOB:
|
||||
log.w( "column %s is blob.", names.get( i ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
writer.endObject();
|
||||
}
|
||||
}finally{
|
||||
cursor.close();
|
||||
}
|
||||
writer.endArray();
|
||||
}
|
||||
|
||||
private static void importTable( JsonReader reader, String table, HashMap< Long, Long > id_map ) throws IOException{
|
||||
SQLiteDatabase db = App1.getDB();
|
||||
if( table.equals( SavedAccount.table ) ){
|
||||
SavedAccount.onDBDelete( db );
|
||||
SavedAccount.onDBCreate( db );
|
||||
}
|
||||
|
||||
boolean bOK = false;
|
||||
db.execSQL( "BEGIN TRANSACTION" );
|
||||
try{
|
||||
db.execSQL( "delete from " + table );
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
|
||||
reader.beginArray();
|
||||
while( reader.hasNext() ){
|
||||
|
||||
long old_id = - 1L;
|
||||
cv.clear();
|
||||
|
||||
reader.beginObject();
|
||||
while( reader.hasNext() ){
|
||||
String name = reader.nextName();
|
||||
|
||||
if( BaseColumns._ID.equals( name ) ){
|
||||
old_id = reader.nextLong();
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonToken token = reader.peek();
|
||||
switch( token ){
|
||||
case NULL:
|
||||
reader.skipValue();
|
||||
cv.putNull( name );
|
||||
break;
|
||||
|
||||
case BOOLEAN:
|
||||
cv.put( name, reader.nextBoolean() ? 1 : 0 );
|
||||
break;
|
||||
|
||||
case NUMBER:
|
||||
cv.put( name, reader.nextLong() );
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
cv.put( name, reader.nextString() );
|
||||
break;
|
||||
|
||||
default:
|
||||
reader.skipValue();
|
||||
// 無視する
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
long new_id = db.insert( table, null, cv );
|
||||
if( id_map != null ) id_map.put( old_id, new_id );
|
||||
}
|
||||
reader.endArray();
|
||||
bOK = true;
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
log.e( ex, "saveList failed." );
|
||||
}
|
||||
if( bOK ){
|
||||
db.execSQL( "COMMIT TRANSACTION" );
|
||||
}else{
|
||||
db.execSQL( "ROLLBACK TRANSACTION" );
|
||||
}
|
||||
}
|
||||
|
||||
private static void writePref( JsonWriter writer, SharedPreferences pref ) throws IOException{
|
||||
writer.beginObject();
|
||||
for( Map.Entry< String, ? > entry : pref.getAll().entrySet() ){
|
||||
String k = entry.getKey();
|
||||
Object v = entry.getValue();
|
||||
writer.name( k );
|
||||
if( v == null ){
|
||||
writer.nullValue();
|
||||
}else if( v instanceof String ){
|
||||
writer.value( (String) v );
|
||||
}else if( v instanceof Boolean ){
|
||||
writer.value( (Boolean) v );
|
||||
}else if( v instanceof Number ){
|
||||
writer.value( (Number) v );
|
||||
}else{
|
||||
throw new RuntimeException( String.format( Locale.JAPAN, "writePref. bad data type: Preference key =%s", k ) );
|
||||
}
|
||||
}
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
private static void importPref( JsonReader reader, SharedPreferences pref ) throws IOException{
|
||||
SharedPreferences.Editor e = pref.edit();
|
||||
reader.beginObject();
|
||||
while( reader.hasNext() ){
|
||||
String k = reader.nextName();
|
||||
if( k == null ){
|
||||
throw new RuntimeException( "importPref: name is null" );
|
||||
}
|
||||
JsonToken token = reader.peek();
|
||||
if( token == JsonToken.NULL ){
|
||||
reader.nextNull();
|
||||
e.remove( k );
|
||||
continue;
|
||||
}
|
||||
switch( k ){
|
||||
// boolean
|
||||
case Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN:
|
||||
case Pref.KEY_PRIOR_LOCAL_URL:
|
||||
case Pref.KEY_DISABLE_FAST_SCROLLER:
|
||||
case Pref.KEY_SIMPLE_LIST:
|
||||
case Pref.KEY_NOTIFICATION_SOUND:
|
||||
case Pref.KEY_NOTIFICATION_VIBRATION:
|
||||
case Pref.KEY_NOTIFICATION_LED:
|
||||
case Pref.KEY_EXIT_APP_WHEN_CLOSE_PROTECTED_COLUMN:
|
||||
case Pref.KEY_SHOW_FOLLOW_BUTTON_IN_BUTTON_BAR:
|
||||
case Pref.KEY_DONT_ROUND:
|
||||
case Pref.KEY_DONT_USE_STREAMING:
|
||||
case Pref.KEY_DONT_REFRESH_ON_RESUME:
|
||||
case Pref.KEY_DONT_SCREEN_OFF:
|
||||
case Pref.KEY_DISABLE_TABLET_MODE:
|
||||
boolean bv = reader.nextBoolean();
|
||||
e.putBoolean( k, bv );
|
||||
break;
|
||||
|
||||
// int
|
||||
case Pref.KEY_BACK_BUTTON_ACTION:
|
||||
case Pref.KEY_UI_THEME:
|
||||
case Pref.KEY_RESIZE_IMAGE:
|
||||
case Pref.KEY_REFRESH_AFTER_TOOT:
|
||||
case Pref.KEY_FOOTER_BUTTON_BG_COLOR:
|
||||
case Pref.KEY_FOOTER_BUTTON_FG_COLOR:
|
||||
case Pref.KEY_FOOTER_TAB_BG_COLOR:
|
||||
case Pref.KEY_FOOTER_TAB_DIVIDER_COLOR:
|
||||
int iv = reader.nextInt();
|
||||
e.putInt( k, iv );
|
||||
break;
|
||||
|
||||
// string
|
||||
case Pref.KEY_COLUMN_WIDTH:
|
||||
case Pref.KEY_MEDIA_THUMB_HEIGHT:
|
||||
String sv = reader.nextString();
|
||||
e.putString( k, sv );
|
||||
break;
|
||||
|
||||
// force reset
|
||||
default:
|
||||
case Pref.KEY_TIMELINE_FONT:
|
||||
reader.skipValue();
|
||||
e.remove( k );
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
e.apply();
|
||||
}
|
||||
|
||||
private static final String KEY_PREF = "pref";
|
||||
private static final String KEY_ACCOUNT = "account";
|
||||
private static final String KEY_COLUMN = "column";
|
||||
private static final String KEY_ACCT_COLOR = "acct_color";
|
||||
private static final String KEY_MUTED_APP = "muted_app";
|
||||
private static final String KEY_MUTED_WORD = "muted_word";
|
||||
|
||||
static void encodeAppData( Context context, JsonWriter writer )
|
||||
throws IOException, JSONException{
|
||||
writer.setIndent( " " );
|
||||
writer.beginObject();
|
||||
|
||||
AppState app_state = App1.getAppState( context );
|
||||
//////////////////////////////////////
|
||||
{
|
||||
writer.name( KEY_PREF );
|
||||
writePref( writer, app_state.pref );
|
||||
}
|
||||
//////////////////////////////////////
|
||||
writeFromTable( writer, KEY_ACCOUNT, SavedAccount.table );
|
||||
writeFromTable( writer, KEY_ACCT_COLOR, AcctColor.table );
|
||||
writeFromTable( writer, KEY_MUTED_APP, MutedApp.table );
|
||||
writeFromTable( writer, KEY_MUTED_WORD, MutedWord.table );
|
||||
|
||||
//////////////////////////////////////
|
||||
{
|
||||
writer.name( KEY_COLUMN );
|
||||
writer.beginArray();
|
||||
for( Column column : app_state.column_list ){
|
||||
JSONObject dst = new JSONObject();
|
||||
column.encodeJSON( dst, 0 );
|
||||
writeJSONObject( writer, dst );
|
||||
}
|
||||
writer.endArray();
|
||||
}
|
||||
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
static ArrayList< Column > decodeAppData( Context context, JsonReader reader )
|
||||
throws IOException, JSONException{
|
||||
|
||||
ArrayList< Column > result = null;
|
||||
|
||||
AppState app_state = App1.getAppState( context );
|
||||
reader.beginObject();
|
||||
|
||||
@SuppressLint("UseSparseArrays")
|
||||
HashMap< Long, Long > account_id_map = new HashMap<>();
|
||||
|
||||
while( reader.hasNext() ){
|
||||
String name = reader.nextName();
|
||||
|
||||
if( KEY_PREF.equals( name ) ){
|
||||
importPref( reader, app_state.pref );
|
||||
|
||||
}else if( KEY_ACCOUNT.equals( name ) ){
|
||||
importTable( reader, SavedAccount.table, account_id_map );
|
||||
|
||||
}else if( KEY_ACCT_COLOR.equals( name ) ){
|
||||
importTable( reader, AcctColor.table, null );
|
||||
AcctColor.clearMemoryCache();
|
||||
}else if( KEY_MUTED_APP.equals( name ) ){
|
||||
importTable( reader, MutedApp.table, null );
|
||||
|
||||
}else if( KEY_MUTED_WORD.equals( name ) ){
|
||||
importTable( reader, MutedWord.table, null );
|
||||
|
||||
}else if( KEY_COLUMN.equals( name ) ){
|
||||
result = importColumn( app_state, reader, account_id_map );
|
||||
}
|
||||
}
|
||||
|
||||
if( result == null ){
|
||||
throw new RuntimeException( "import data does not includes column list!" );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static @NonNull
|
||||
ArrayList< Column > importColumn( AppState app_state, JsonReader reader, HashMap< Long, Long > id_map ) throws IOException, JSONException{
|
||||
ArrayList< Column > result = new ArrayList<>();
|
||||
reader.beginArray();
|
||||
while( reader.hasNext() ){
|
||||
JSONObject item = readJsonObject( reader );
|
||||
long old_id = item.optLong( Column.KEY_ACCOUNT_ROW_ID, - 1L );
|
||||
Long new_id = id_map.get( old_id );
|
||||
if( new_id == null ){
|
||||
throw new RuntimeException( "importColumn: can't convert account id" );
|
||||
}
|
||||
item.put( Column.KEY_ACCOUNT_ROW_ID, (long) new_id );
|
||||
result.add( new Column( app_state, item ) );
|
||||
}
|
||||
reader.endArray();
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -108,7 +108,7 @@ class Column implements StreamReader.Callback {
|
|||
static final String PATH_SEARCH = "/api/v1/search?q=%s"; // 1: query(urlencoded) , also, append "&resolve=1" if resolve non-local accounts
|
||||
private static final String PATH_INSTANCE = "/api/v1/instance";
|
||||
|
||||
private static final String KEY_ACCOUNT_ROW_ID = "account_id";
|
||||
static final String KEY_ACCOUNT_ROW_ID = "account_id";
|
||||
static final String KEY_TYPE = "type";
|
||||
static final String KEY_DONT_CLOSE = "dont_close";
|
||||
private static final String KEY_WITH_ATTACHMENT = "with_attachment";
|
||||
|
|
|
@ -18,6 +18,7 @@ public class Pref {
|
|||
static final int RAT_DONT_REFRESH = 2;
|
||||
|
||||
static final String KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN = "DontConfirmBeforeCloseColumn";
|
||||
|
||||
static final String KEY_BACK_BUTTON_ACTION = "back_button_action";
|
||||
static final String KEY_PRIOR_LOCAL_URL = "prior_local_url";
|
||||
static final String KEY_DISABLE_FAST_SCROLLER = "disable_fast_scroller";
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.database.Cursor;
|
|||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.AppDataExporter;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
|
@ -12,14 +13,16 @@ import android.support.annotation.NonNull;
|
|||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.util.LruCache;
|
||||
import android.text.TextUtils;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
public class AcctColor {
|
||||
|
||||
private static final LogCategory log = new LogCategory( "AcctColor" );
|
||||
|
||||
private static final String table = "acct_color";
|
||||
public static final String table = "acct_color";
|
||||
private static final String COL_TIME_SAVE = "time_save";
|
||||
private static final String COL_ACCT = "ac"; //@who@host ascii文字の大文字小文字は(sqliteにより)同一視される
|
||||
private static final String COL_COLOR_FG = "cf"; // 未設定なら0、それ以外は色
|
||||
|
@ -69,9 +72,9 @@ public class AcctColor {
|
|||
}
|
||||
|
||||
public void save( long now ){
|
||||
|
||||
|
||||
acct = acct.toLowerCase( Locale.ENGLISH );
|
||||
|
||||
|
||||
try{
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put( COL_TIME_SAVE, now );
|
||||
|
@ -112,7 +115,7 @@ public class AcctColor {
|
|||
if( cursor != null ){
|
||||
try{
|
||||
if( cursor.moveToNext() ){
|
||||
dst = new AcctColor(acct);
|
||||
dst = new AcctColor( acct );
|
||||
int idx;
|
||||
|
||||
idx = cursor.getColumnIndex( COL_COLOR_FG );
|
||||
|
@ -135,7 +138,7 @@ public class AcctColor {
|
|||
ex.printStackTrace();
|
||||
log.e( ex, "load failed." );
|
||||
}
|
||||
log.d("lruCache size=%s,hit=%s,miss=%s",mMemoryCache.size(),mMemoryCache.hitCount(),mMemoryCache.missCount() );
|
||||
log.d( "lruCache size=%s,hit=%s,miss=%s", mMemoryCache.size(), mMemoryCache.hitCount(), mMemoryCache.missCount() );
|
||||
dst = new AcctColor( acct );
|
||||
mMemoryCache.put( acct, dst );
|
||||
return dst;
|
||||
|
@ -157,4 +160,8 @@ public class AcctColor {
|
|||
public static boolean hasColorBackground( @Nullable AcctColor ac ){
|
||||
return ac != null && ac.color_bg != 0;
|
||||
}
|
||||
|
||||
public static void clearMemoryCache(){
|
||||
mMemoryCache.evictAll ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,22 +3,25 @@ package jp.juggler.subwaytooter.table;
|
|||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.AppDataExporter;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
|
||||
public class MutedApp {
|
||||
|
||||
private static final LogCategory log = new LogCategory( "MutedApp" );
|
||||
|
||||
private static final String table = "app_mute";
|
||||
public static final String table = "app_mute";
|
||||
public static final String COL_NAME = "name";
|
||||
private static final String COL_TIME_SAVE = "time_save";
|
||||
|
||||
public static void onDBCreate( SQLiteDatabase db ){
|
||||
log.d("onDBCreate!");
|
||||
log.d( "onDBCreate!" );
|
||||
db.execSQL(
|
||||
"create table if not exists " + table
|
||||
+ "(_id INTEGER PRIMARY KEY"
|
||||
|
@ -32,7 +35,7 @@ public class MutedApp {
|
|||
}
|
||||
|
||||
public static void onDBUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ){
|
||||
if(oldVersion < 6 && newVersion >= 6){
|
||||
if( oldVersion < 6 && newVersion >= 6 ){
|
||||
onDBCreate( db );
|
||||
}
|
||||
}
|
||||
|
@ -53,62 +56,62 @@ public class MutedApp {
|
|||
}
|
||||
|
||||
public static Cursor createCursor(){
|
||||
return App1.getDB().query( table, null,null,null, null, null, COL_NAME+" asc" );
|
||||
return App1.getDB().query( table, null, null, null, null, null, COL_NAME + " asc" );
|
||||
}
|
||||
|
||||
public static void delete( String name ){
|
||||
try{
|
||||
App1.getDB().delete( table, COL_NAME+"=?",new String[]{ name });
|
||||
App1.getDB().delete( table, COL_NAME + "=?", new String[]{ name } );
|
||||
}catch( Throwable ex ){
|
||||
log.e( ex, "delete failed." );
|
||||
}
|
||||
}
|
||||
|
||||
public static HashSet<String> getNameSet(){
|
||||
HashSet<String> dst = new HashSet<>();
|
||||
public static HashSet< String > getNameSet(){
|
||||
HashSet< String > dst = new HashSet<>();
|
||||
try{
|
||||
Cursor cursor = App1.getDB().query( table, null,null,null, null, null, null);
|
||||
Cursor cursor = App1.getDB().query( table, null, null, null, null, null, null );
|
||||
if( cursor != null ){
|
||||
try{
|
||||
int idx_name = cursor.getColumnIndex( COL_NAME );
|
||||
while(cursor.moveToNext()){
|
||||
String s = cursor.getString(idx_name);
|
||||
dst.add( s);
|
||||
while( cursor.moveToNext() ){
|
||||
String s = cursor.getString( idx_name );
|
||||
dst.add( s );
|
||||
}
|
||||
}finally{
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}catch(Throwable ex){
|
||||
ex.printStackTrace( );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// private static final String[] isMuted_projection = new String[]{COL_NAME};
|
||||
// private static final String isMuted_where = COL_NAME+"=?";
|
||||
// private static final ThreadLocal<String[]> isMuted_where_arg = new ThreadLocal<String[]>() {
|
||||
// @Override protected String[] initialValue() {
|
||||
// return new String[1];
|
||||
// }
|
||||
// };
|
||||
// public static boolean isMuted( String app_name ){
|
||||
// if( app_name == null ) return false;
|
||||
// try{
|
||||
// String[] where_arg = isMuted_where_arg.get();
|
||||
// where_arg[0] = app_name;
|
||||
// Cursor cursor = App1.getDB().query( table, isMuted_projection,isMuted_where , where_arg, null, null, null );
|
||||
// try{
|
||||
// if( cursor.moveToFirst() ){
|
||||
// return true;
|
||||
// }
|
||||
// }finally{
|
||||
// cursor.close();
|
||||
// }
|
||||
// }catch( Throwable ex ){
|
||||
// log.e( ex, "load failed." );
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// private static final String[] isMuted_projection = new String[]{COL_NAME};
|
||||
// private static final String isMuted_where = COL_NAME+"=?";
|
||||
// private static final ThreadLocal<String[]> isMuted_where_arg = new ThreadLocal<String[]>() {
|
||||
// @Override protected String[] initialValue() {
|
||||
// return new String[1];
|
||||
// }
|
||||
// };
|
||||
// public static boolean isMuted( String app_name ){
|
||||
// if( app_name == null ) return false;
|
||||
// try{
|
||||
// String[] where_arg = isMuted_where_arg.get();
|
||||
// where_arg[0] = app_name;
|
||||
// Cursor cursor = App1.getDB().query( table, isMuted_projection,isMuted_where , where_arg, null, null, null );
|
||||
// try{
|
||||
// if( cursor.moveToFirst() ){
|
||||
// return true;
|
||||
// }
|
||||
// }finally{
|
||||
// cursor.close();
|
||||
// }
|
||||
// }catch( Throwable ex ){
|
||||
// log.e( ex, "load failed." );
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -3,22 +3,25 @@ package jp.juggler.subwaytooter.table;
|
|||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.AppDataExporter;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
|
||||
public class MutedWord {
|
||||
|
||||
private static final LogCategory log = new LogCategory( "MutedWord" );
|
||||
|
||||
private static final String table = "word_mute";
|
||||
public static final String table = "word_mute";
|
||||
public static final String COL_NAME = "name";
|
||||
private static final String COL_TIME_SAVE = "time_save";
|
||||
|
||||
public static void onDBCreate( SQLiteDatabase db ){
|
||||
log.d("onDBCreate!");
|
||||
log.d( "onDBCreate!" );
|
||||
db.execSQL(
|
||||
"create table if not exists " + table
|
||||
+ "(_id INTEGER PRIMARY KEY"
|
||||
|
@ -32,7 +35,7 @@ public class MutedWord {
|
|||
}
|
||||
|
||||
public static void onDBUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ){
|
||||
if(oldVersion < 11 && newVersion >= 11){
|
||||
if( oldVersion < 11 && newVersion >= 11 ){
|
||||
onDBCreate( db );
|
||||
}
|
||||
}
|
||||
|
@ -53,62 +56,62 @@ public class MutedWord {
|
|||
}
|
||||
|
||||
public static Cursor createCursor(){
|
||||
return App1.getDB().query( table, null,null,null, null, null, COL_NAME+" asc" );
|
||||
return App1.getDB().query( table, null, null, null, null, null, COL_NAME + " asc" );
|
||||
}
|
||||
|
||||
public static void delete( String name ){
|
||||
try{
|
||||
App1.getDB().delete( table, COL_NAME+"=?",new String[]{ name });
|
||||
App1.getDB().delete( table, COL_NAME + "=?", new String[]{ name } );
|
||||
}catch( Throwable ex ){
|
||||
log.e( ex, "delete failed." );
|
||||
}
|
||||
}
|
||||
|
||||
public static HashSet<String> getNameSet(){
|
||||
HashSet<String> dst = new HashSet<>();
|
||||
public static HashSet< String > getNameSet(){
|
||||
HashSet< String > dst = new HashSet<>();
|
||||
try{
|
||||
Cursor cursor = App1.getDB().query( table, null,null,null, null, null, null);
|
||||
Cursor cursor = App1.getDB().query( table, null, null, null, null, null, null );
|
||||
if( cursor != null ){
|
||||
try{
|
||||
int idx_name = cursor.getColumnIndex( COL_NAME );
|
||||
while(cursor.moveToNext()){
|
||||
String s = cursor.getString(idx_name);
|
||||
dst.add( s);
|
||||
while( cursor.moveToNext() ){
|
||||
String s = cursor.getString( idx_name );
|
||||
dst.add( s );
|
||||
}
|
||||
}finally{
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}catch(Throwable ex){
|
||||
ex.printStackTrace( );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// private static final String[] isMuted_projection = new String[]{COL_NAME};
|
||||
// private static final String isMuted_where = COL_NAME+"=?";
|
||||
// private static final ThreadLocal<String[]> isMuted_where_arg = new ThreadLocal<String[]>() {
|
||||
// @Override protected String[] initialValue() {
|
||||
// return new String[1];
|
||||
// }
|
||||
// };
|
||||
// public static boolean isMuted( String app_name ){
|
||||
// if( app_name == null ) return false;
|
||||
// try{
|
||||
// String[] where_arg = isMuted_where_arg.get();
|
||||
// where_arg[0] = app_name;
|
||||
// Cursor cursor = App1.getDB().query( table, isMuted_projection,isMuted_where , where_arg, null, null, null );
|
||||
// try{
|
||||
// if( cursor.moveToFirst() ){
|
||||
// return true;
|
||||
// }
|
||||
// }finally{
|
||||
// cursor.close();
|
||||
// }
|
||||
// }catch( Throwable ex ){
|
||||
// log.e( ex, "load failed." );
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// private static final String[] isMuted_projection = new String[]{COL_NAME};
|
||||
// private static final String isMuted_where = COL_NAME+"=?";
|
||||
// private static final ThreadLocal<String[]> isMuted_where_arg = new ThreadLocal<String[]>() {
|
||||
// @Override protected String[] initialValue() {
|
||||
// return new String[1];
|
||||
// }
|
||||
// };
|
||||
// public static boolean isMuted( String app_name ){
|
||||
// if( app_name == null ) return false;
|
||||
// try{
|
||||
// String[] where_arg = isMuted_where_arg.get();
|
||||
// where_arg[0] = app_name;
|
||||
// Cursor cursor = App1.getDB().query( table, isMuted_projection,isMuted_where , where_arg, null, null, null );
|
||||
// try{
|
||||
// if( cursor.moveToFirst() ){
|
||||
// return true;
|
||||
// }
|
||||
// }finally{
|
||||
// cursor.close();
|
||||
// }
|
||||
// }catch( Throwable ex ){
|
||||
// log.e( ex, "load failed." );
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ import jp.juggler.subwaytooter.util.LogCategory;
|
|||
public class SavedAccount extends TootAccount implements LinkClickContext {
|
||||
private static final LogCategory log = new LogCategory( "SavedAccount" );
|
||||
|
||||
private static final String table = "access_info";
|
||||
public static final String table = "access_info";
|
||||
|
||||
private static final String COL_ID = BaseColumns._ID;
|
||||
public static final String COL_ID = BaseColumns._ID;
|
||||
private static final String COL_HOST = "h";
|
||||
private static final String COL_USER = "u";
|
||||
private static final String COL_ACCOUNT = "a";
|
||||
|
@ -65,6 +65,15 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
|||
public boolean confirm_unfollow;
|
||||
public boolean confirm_post;
|
||||
|
||||
// アプリデータのインポート時に呼ばれる
|
||||
public static void onDBDelete( SQLiteDatabase db ){
|
||||
try{
|
||||
db.execSQL( "drop table if exists " + table );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void onDBCreate( SQLiteDatabase db ){
|
||||
db.execSQL(
|
||||
"create table if not exists " + table
|
||||
|
@ -298,7 +307,7 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
|||
public String getUserUrl( @NonNull String who_acct ){
|
||||
int p = who_acct.indexOf( '@' );
|
||||
if( - 1 != p ){
|
||||
return "https://" +who_acct.substring( p + 1 ) + "/@" + who_acct.substring( 0,p);
|
||||
return "https://" + who_acct.substring( p + 1 ) + "/@" + who_acct.substring( 0, p );
|
||||
}else{
|
||||
return "https://" + host + "/@" + who_acct;
|
||||
}
|
||||
|
@ -345,10 +354,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
|||
|
||||
public static long getCount(){
|
||||
try{
|
||||
Cursor cursor = App1.getDB().query( table,new String[]{ "count(*)"} , null, null, null, null, null );
|
||||
Cursor cursor = App1.getDB().query( table, new String[]{ "count(*)" }, null, null, null, null, null );
|
||||
try{
|
||||
if( cursor.moveToNext() ){
|
||||
return cursor.getLong(0);
|
||||
return cursor.getLong( 0 );
|
||||
}
|
||||
}finally{
|
||||
cursor.close();
|
||||
|
@ -360,4 +369,5 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
|||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -585,6 +585,45 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/actions"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSettingExport"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_data_export"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
<Button
|
||||
android:id="@+id/btnSettingImport"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_data_import"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_data_import_desc"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
<!--<TextView-->
|
||||
<!--style="@style/setting_row_label"-->
|
||||
|
|
|
@ -322,9 +322,13 @@
|
|||
<string name="reply_to_in_draft_is_lost">The reply origin referred by draft has been lost. The following context is removed. in_reply_to</string>
|
||||
<string name="restore_draft">Restore from draft</string>
|
||||
<string name="select_draft">Which draft to restore ?</string>
|
||||
<string name="app_data_export">Export app data</string>
|
||||
<string name="app_data_import">Import app data</string>
|
||||
<string name="app_data_import_confirm">existing data ( before import ) will be cleared. Are you sure?</string>
|
||||
<string name="app_data_import_desc">You can export data to any app that can receive data, But maybe you can import data only from device\'s storage or Google drive.</string>
|
||||
|
||||
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->
|
||||
<!--<string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>-->
|
||||
<!--<string name="abc_action_bar_up_description">Revenir en haut de la page</string>-->
|
||||
|
|
|
@ -610,4 +610,8 @@
|
|||
<string name="reply_to_in_draft_is_lost">下書きから参照されている返信元の投稿は失われました。以下の文脈を再現できません。in_reply_to</string>
|
||||
<string name="restore_draft">下書きから復元</string>
|
||||
<string name="select_draft">どの下書きから復元しますか?</string>
|
||||
<string name="app_data_export">アプリデータのエクスポート</string>
|
||||
<string name="app_data_import">アプリデータのインポート</string>
|
||||
<string name="app_data_import_confirm">アプリに今あるデータは消えてしまいます。よろしいですか?</string>
|
||||
<string name="app_data_import_desc">エクスポート先はいくつか選べますが、インポートするには端末のストレージかGoogleドライブにデータを置く必要があります。カラム背景画像とタイムラインのフォントはエクスポート/インポートの対象外です。</string>
|
||||
</resources>
|
||||
|
|
|
@ -319,4 +319,8 @@
|
|||
<string name="account_in_draft_is_lost">The account used for this draft has been removed from app. The following context can not be restored. in_reply_to, media attachment.</string>
|
||||
<string name="reply_to_in_draft_is_lost">The reply origin referred by draft has been lost. The following context is removed. in_reply_to</string>
|
||||
<string name="attachment_in_draft_is_lost">Some of media attachment in the draft have been lost.</string>
|
||||
<string name="app_data_export">Export app data</string>
|
||||
<string name="app_data_import">Import app data</string>
|
||||
<string name="app_data_import_desc">You can export data to any app that can receive data, But maybe you can import data only from device\'s storage or Google drive.\nColumn background image may be not exported/imported over device.\nTimeline font is not exported/imported.</string>
|
||||
<string name="app_data_import_confirm">existing data ( before import ) will be cleared. Are you sure?</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!--<files-path name="pathFiles" path="."/>-->
|
||||
<cache-path name="pathCache" path="." />
|
||||
<!--<external-path name="pathExternal" path="." />-->
|
||||
<!--<external-files-path name="pathExternalFile" path="." />-->
|
||||
<!--<external-cache-path name="pathExternalCache" path="." />-->
|
||||
<!--<root-path name="pathRoot" path="." />-->
|
||||
</paths>
|
Loading…
Reference in New Issue