public class Authenticator extends AbstractAccountAuthenticator { public Authenticator(Context context) { super(context); } @Override public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { throw new UnsupportedOperationException(); } @Override public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { throw new UnsupportedOperationException(); } @Override public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException { throw new UnsupportedOperationException(); } @Override public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { throw new UnsupportedOperationException(); } @Override public String getAuthTokenLabel(String authTokenType) { throw new UnsupportedOperationException(); } @Override public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { throw new UnsupportedOperationException(); } @Override public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { throw new UnsupportedOperationException(); } }
<?xml version="1.0" encoding="utf-8"?> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountPreferences="@xml/account_prefs" android:accountType="com.elegion.newsfeed.account" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:smallIcon="@drawable/ic_launcher" />
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:persistent="true"> <PreferenceCategory android:title="@string/general_settings" /> <PreferenceScreen android:key="com.elegion.newsfeed.KEY_ACCOUNT_SYNC" android:summary="@string/sync_settings_summary" android:title="@string/sync"> <intent android:action="com.elegion.newsfeed.ACTION_SYNC_SETTINGS" android:targetClass="com.elegion.newsfeed.activity.SyncSettingsActivity" android:targetPackage="com.elegion.newsfeed" /> </PreferenceScreen> </PreferenceScreen>
public class SQLiteContentProvider extends ContentProvider { private static final String DATABASE_NAME = "newsfeed.db"; private static final int DATABASE_VERSION = 1; private static final String MIME_DIR = "vnd.android.cursor.dir/"; private static final String MIME_ITEM = "vnd.android.cursor.item/"; private static final Map<String, SQLiteTableProvider> SCHEMA = new ConcurrentHashMap<>(); static { SCHEMA.put(FeedProvider.TABLE_NAME, new FeedProvider()); SCHEMA.put(NewsProvider.TABLE_NAME, new NewsProvider()); } private final SQLiteUriMatcher mUriMatcher = new SQLiteUriMatcher(); private SQLiteOpenHelper mHelper; private static ProviderInfo getProviderInfo(Context context, Class<? extends ContentProvider> provider, int flags) throws PackageManager.NameNotFoundException { return context.getPackageManager() .getProviderInfo(new ComponentName(context.getPackageName(), provider.getName()), flags); } private static String getTableName(Uri uri) { return uri.getPathSegments().get(0); } @Override public boolean onCreate() { try { final ProviderInfo pi = getProviderInfo(getContext(), getClass(), 0); final String[] authorities = TextUtils.split(pi.authority, ";"); for (final String authority : authorities) { mUriMatcher.addAuthority(authority); } mHelper = new SQLiteOpenHelperImpl(getContext()); return true; } catch (PackageManager.NameNotFoundException e) { throw new SQLiteException(e.getMessage()); } } @Override public Cursor query(Uri uri, String[] columns, String where, String[] whereArgs, String orderBy) { final int matchResult = mUriMatcher.match(uri); if (matchResult == SQLiteUriMatcher.NO_MATCH) { throw new SQLiteException("Unknown uri " + uri); } final String tableName = getTableName(uri); final SQLiteTableProvider tableProvider = SCHEMA.get(tableName); if (tableProvider == null) { throw new SQLiteException("No such table " + tableName); } if (matchResult == SQLiteUriMatcher.MATCH_ID) { where = BaseColumns._ID + "=?"; whereArgs = new String[]{uri.getLastPathSegment()}; } final Cursor cursor = tableProvider.query(mHelper.getReadableDatabase(), columns, where, whereArgs, orderBy); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; } @Override public String getType(Uri uri) { final int matchResult = mUriMatcher.match(uri); if (matchResult == SQLiteUriMatcher.NO_MATCH) { throw new SQLiteException("Unknown uri " + uri); } else if (matchResult == SQLiteUriMatcher.MATCH_ID) { return MIME_ITEM + getTableName(uri); } return MIME_DIR + getTableName(uri); } @Override public Uri insert(Uri uri, ContentValues values) { final int matchResult = mUriMatcher.match(uri); if (matchResult == SQLiteUriMatcher.NO_MATCH) { throw new SQLiteException("Unknown uri " + uri); } final String tableName = getTableName(uri); final SQLiteTableProvider tableProvider = SCHEMA.get(tableName); if (tableProvider == null) { throw new SQLiteException("No such table " + tableName); } if (matchResult == SQLiteUriMatcher.MATCH_ID) { final int affectedRows = updateInternal( tableProvider.getBaseUri(), tableProvider, values, BaseColumns._ID + "=?", new String[]{uri.getLastPathSegment()} ); if (affectedRows > 0) { return uri; } } final long lastId = tableProvider.insert(mHelper.getWritableDatabase(), values); getContext().getContentResolver().notifyChange(tableProvider.getBaseUri(), null); final Bundle extras = new Bundle(); extras.putLong(SQLiteOperation.KEY_LAST_ID, lastId); tableProvider.onContentChanged(getContext(), SQLiteOperation.INSERT, extras); return uri; } @Override public int delete(Uri uri, String where, String[] whereArgs) { final int matchResult = mUriMatcher.match(uri); if (matchResult == SQLiteUriMatcher.NO_MATCH) { throw new SQLiteException("Unknown uri " + uri); } final String tableName = getTableName(uri); final SQLiteTableProvider tableProvider = SCHEMA.get(tableName); if (tableProvider == null) { throw new SQLiteException("No such table " + tableName); } if (matchResult == SQLiteUriMatcher.MATCH_ID) { where = BaseColumns._ID + "=?"; whereArgs = new String[]{uri.getLastPathSegment()}; } final int affectedRows = tableProvider.delete(mHelper.getWritableDatabase(), where, whereArgs); if (affectedRows > 0) { getContext().getContentResolver().notifyChange(uri, null); final Bundle extras = new Bundle(); extras.putLong(SQLiteOperation.KEY_AFFECTED_ROWS, affectedRows); tableProvider.onContentChanged(getContext(), SQLiteOperation.DELETE, extras); } return affectedRows; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { final int matchResult = mUriMatcher.match(uri); if (matchResult == SQLiteUriMatcher.NO_MATCH) { throw new SQLiteException("Unknown uri " + uri); } final String tableName = getTableName(uri); final SQLiteTableProvider tableProvider = SCHEMA.get(tableName); if (tableProvider == null) { throw new SQLiteException("No such table " + tableName); } if (matchResult == SQLiteUriMatcher.MATCH_ID) { where = BaseColumns._ID + "=?"; whereArgs = new String[]{uri.getLastPathSegment()}; } return updateInternal(tableProvider.getBaseUri(), tableProvider, values, where, whereArgs); } private int updateInternal(Uri uri, SQLiteTableProvider provider, ContentValues values, String where, String[] whereArgs) { final int affectedRows = provider.update(mHelper.getWritableDatabase(), values, where, whereArgs); if (affectedRows > 0) { getContext().getContentResolver().notifyChange(uri, null); final Bundle extras = new Bundle(); extras.putLong(SQLiteOperation.KEY_AFFECTED_ROWS, affectedRows); provider.onContentChanged(getContext(), SQLiteOperation.UPDATE, extras); } return affectedRows; } private static final class SQLiteOpenHelperImpl extends SQLiteOpenHelper { public SQLiteOpenHelperImpl(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.beginTransactionNonExclusive(); try { for (final SQLiteTableProvider table : SCHEMA.values()) { table.onCreate(db); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.beginTransactionNonExclusive(); try { for (final SQLiteTableProvider table : SCHEMA.values()) { table.onUpgrade(db, oldVersion, newVersion); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } } }
<provider android:name=".sqlite.SQLiteContentProvider" android:authorities="com.elegion.newsfeed" android:exported="false" android:syncable="true" />
public class FeedProvider extends SQLiteTableProvider { public static final String TABLE_NAME = "feeds"; public static final Uri URI = Uri.parse("content://com.elegion.newsfeed/" + TABLE_NAME); public FeedProvider() { super(TABLE_NAME); } public static long getId(Cursor c) { return c.getLong(c.getColumnIndex(Columns._ID)); } public static String getIconUrl(Cursor c) { return c.getString(c.getColumnIndex(Columns.IMAGE_URL)); } public static String getTitle(Cursor c) { return c.getString(c.getColumnIndex(Columns.TITLE)); } public static String getLink(Cursor c) { return c.getString(c.getColumnIndex(Columns.LINK)); } public static long getPubDate(Cursor c) { return c.getLong(c.getColumnIndex(Columns.PUB_DATE)); } public static String getRssLink(Cursor c) { return c.getString(c.getColumnIndex(Columns.RSS_LINK)); } @Override public Uri getBaseUri() { return URI; } @Override public void onContentChanged(Context context, int operation, Bundle extras) { if (operation == INSERT) { extras.keySet(); final Bundle syncExtras = new Bundle(); syncExtras.putLong(SyncAdapter.KEY_FEED_ID, extras.getLong(KEY_LAST_ID, -1)); ContentResolver.requestSync(AppDelegate.sAccount, AppDelegate.AUTHORITY, syncExtras); } } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table if not exists " + TABLE_NAME + "(" + Columns._ID + " integer primary key on conflict replace, " + Columns.TITLE + " text, " + Columns.LINK + " text, " + Columns.IMAGE_URL + " text, " + Columns.LANGUAGE + " text, " + Columns.PUB_DATE + " integer, " + Columns.RSS_LINK + " text unique on conflict ignore)"); db.execSQL("create trigger if not exists after delete on " + TABLE_NAME + " begin " + " delete from " + NewsProvider.TABLE_NAME + " where " + NewsProvider.Columns.FEED_ID + "=old." + Columns._ID + ";" + " end;"); } public interface Columns extends BaseColumns { String TITLE = "title"; String LINK = "link"; String IMAGE_URL = "imageUrl"; String LANGUAGE = "language"; String PUB_DATE = "pubDate"; String RSS_LINK = "rssLink"; } }
public class SyncAdapter extends AbstractThreadedSyncAdapter { public static final String KEY_FEED_ID = "com.elegion.newsfeed.sync.KEY_FEED_ID"; public SyncAdapter(Context context) { super(context, true); } @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { final long feedId = extras.getLong(KEY_FEED_ID, -1); if (feedId > 0) { syncFeeds(provider, syncResult, FeedProvider.Columns._ID + "=?", new String[]{String.valueOf(feedId)}); } else { syncFeeds(provider, syncResult, null, null); } } private void syncFeeds(ContentProviderClient provider, SyncResult syncResult, String where, String[] whereArgs) { try { final Cursor feeds = provider.query( FeedProvider.URI, new String[]{ FeedProvider.Columns._ID, FeedProvider.Columns.RSS_LINK }, where, whereArgs, null ); try { if (feeds.moveToFirst()) { do { syncFeed(feeds.getString(0), feeds.getString(1), provider, syncResult); } while (feeds.moveToNext()); } } finally { feeds.close(); } } catch (RemoteException e) { Log.e(SyncAdapter.class.getName(), e.getMessage(), e); ++syncResult.stats.numIoExceptions; } } private void syncFeed(String feedId, String feedUrl, ContentProviderClient provider, SyncResult syncResult) { try { final HttpURLConnection cn = (HttpURLConnection) new URL(feedUrl).openConnection(); try { final RssFeedParser parser = new RssFeedParser(cn.getInputStream()); try { parser.parse(feedId, provider, syncResult); } finally { parser.close(); } } finally { cn.disconnect(); } } catch (IOException e) { Log.e(SyncAdapter.class.getName(), e.getMessage(), e); ++syncResult.stats.numIoExceptions; } } }
public class SyncService extends Service { private static SyncAdapter sSyncAdapter; @Override public void onCreate() { super.onCreate(); if (sSyncAdapter == null) { sSyncAdapter = new SyncAdapter(getApplicationContext()); } } @Override public IBinder onBind(Intent intent) { return sSyncAdapter.getSyncAdapterBinder(); } }
<?xml version="1.0" encoding="utf-8"?> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="com.elegion.newsfeed.account" android:allowParallelSyncs="false" android:contentAuthority="com.elegion.newsfeed" android:isAlwaysSyncable="true" android:supportsUploading="false" android:userVisible="true" />
<service android:name=".sync.SyncService" android:exported="false" android:process=":sync"> <intent-filter> <action android:name="android.content.SyncAdapter" /> </intent-filter> <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/sync_adapter" /> </service>
public class SwipeToRefreshList extends Fragment implements SwipeRefreshLayout.OnRefreshListener, SyncStatusObserver, AdapterView.OnItemClickListener, SwipeToDismissCallback { private SwipeRefreshLayout mRefresher; private ListView mListView; private Object mSyncMonitor; private SwipeToDismissController mSwipeToDismissController; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fmt_swipe_to_refresh_list, container, false); mListView = (ListView) view.findViewById(android.R.id.list); return (mRefresher = (SwipeRefreshLayout) view); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mRefresher.setColorScheme( android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_green_light, android.R.color.holo_orange_light ); mSwipeToDismissController = new SwipeToDismissController(mListView, this); } @Override public void onResume() { super.onResume(); mRefresher.setOnRefreshListener(this); mSyncMonitor = ContentResolver.addStatusChangeListener( ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE | ContentResolver.SYNC_OBSERVER_TYPE_PENDING, this ); mListView.setOnItemClickListener(this); mListView.setOnTouchListener(mSwipeToDismissController); mListView.setOnScrollListener(mSwipeToDismissController); } @Override public void onPause() { mRefresher.setOnRefreshListener(null); ContentResolver.removeStatusChangeListener(mSyncMonitor); mListView.setOnItemClickListener(null); mListView.setOnTouchListener(null); mListView.setOnScrollListener(null); super.onPause(); } @Override public final void onRefresh() { onRefresh(AppDelegate.sAccount); } @Override public final void onStatusChanged(int which) { mRefresher.post(new Runnable() { @Override public void run() { onSyncStatusChanged(AppDelegate.sAccount, ContentResolver .isSyncActive(AppDelegate.sAccount, AppDelegate.AUTHORITY)); } }); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } @Override public boolean canDismissView(View view, int position) { return false; } @Override public void dismissView(View view, int position) { } public void setListAdapter(ListAdapter adapter) { final DataSetObserver dataSetObserver = mSwipeToDismissController.getDataSetObserver(); final ListAdapter oldAdapter = mListView.getAdapter(); if (oldAdapter != null) { oldAdapter.unregisterDataSetObserver(dataSetObserver); } mListView.setAdapter(adapter); adapter.registerDataSetObserver(dataSetObserver); } protected void onRefresh(Account account) { } protected void onSyncStatusChanged(Account account, boolean isSyncActive) { } protected void setRefreshing(boolean refreshing) { mRefresher.setRefreshing(refreshing); } }
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:key="com.elegion.newsfeed.KEY_SYNC" android:title="@string/sync"> <CheckBoxPreference android:defaultValue="false" android:key="com.elegion.newsfeed.KEY_AUTO_SYNC" android:summary="@string/auto_sync_summary" android:title="@string/auto_sync" /> <ListPreference android:defaultValue="@string/auto_sync_interval_default" android:dependency="com.elegion.newsfeed.KEY_AUTO_SYNC" android:entries="@array/auto_sync_intervals" android:entryValues="@array/auto_sync_interval_values" android:key="com.elegion.newsfeed.KEY_AUTO_SYNC_INTERVAL" android:title="@string/auto_sync_interval" /> </PreferenceCategory> </PreferenceScreen>
public class SyncSettings extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String KEY_AUTO_SYNC = "com.elegion.newsfeed.KEY_AUTO_SYNC"; private static final String KEY_AUTO_SYNC_INTERVAL = "com.elegion.newsfeed.KEY_AUTO_SYNC_INTERVAL"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.sync_prefs); final ListPreference interval = (ListPreference) getPreferenceManager() .findPreference(KEY_AUTO_SYNC_INTERVAL); interval.setSummary(interval.getEntry()); } @Override public void onResume() { super.onResume(); getPreferenceManager().getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); } @Override public void onPause() { getPreferenceManager().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); super.onPause(); } @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if (TextUtils.equals(KEY_AUTO_SYNC, key)) { if (prefs.getBoolean(key, false)) { final long interval = Long.parseLong(prefs.getString( KEY_AUTO_SYNC_INTERVAL, getString(R.string.auto_sync_interval_default) )); ContentResolver.addPeriodicSync(AppDelegate.sAccount, AppDelegate.AUTHORITY, Bundle.EMPTY, interval); } else { ContentResolver.removePeriodicSync(AppDelegate.sAccount, AppDelegate.AUTHORITY, new Bundle()); } } else if (TextUtils.equals(KEY_AUTO_SYNC_INTERVAL, key)) { final ListPreference interval = (ListPreference) getPreferenceManager().findPreference(key); interval.setSummary(interval.getEntry()); ContentResolver.addPeriodicSync( AppDelegate.sAccount, AppDelegate.AUTHORITY, Bundle.EMPTY, Long.parseLong(interval.getValue()) ); } } }
Source: https://habr.com/ru/post/216857/
All Articles