/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.basicsyncadapter; import android.accounts.Account; import android.annotation.TargetApi; import android.app.Activity; import android.content.ContentResolver; import android.content.Intent; import android.content.SyncStatusObserver; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.widget.SimpleCursorAdapter; import android.text.format.Time; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ListView; import android.widget.TextView; import com.example.android.common.accounts.GenericAccountService; import com.example.android.basicsyncadapter.provider.FeedContract; /** * List fragment containing a list of Atom entry objects (articles) stored in the local database. * *
Database access is mediated by a content provider, specified in * {@link com.example.android.basicsyncadapter.provider.FeedProvider}. This content * provider is * automatically populated by {@link SyncService}. * *
Selecting an item from the displayed list displays the article in the default browser. * *
If the content provider doesn't return any data, then the first sync hasn't run yet. This sync * adapter assumes data exists in the provider once a sync has run. If your app doesn't work like * this, you should add a flag that notes if a sync has run, so you can differentiate between "no * available data" and "no initial sync", and display this in the UI. * *
The ActionBar displays a "Refresh" button. When the user clicks "Refresh", the sync adapter
* runs immediately. An indeterminate ProgressBar element is displayed, showing that the sync is
* occurring.
*/
public class EntryListFragment extends ListFragment
implements LoaderManager.LoaderCallbacks This allows us to delete our SyncObserver once the application is no longer in the
* foreground.
*/
private Object mSyncObserverHandle;
/**
* Options menu used to populate ActionBar.
*/
private Menu mOptionsMenu;
/**
* Projection for querying the content provider.
*/
private static final String[] PROJECTION = new String[]{
FeedContract.Entry._ID,
FeedContract.Entry.COLUMN_NAME_TITLE,
FeedContract.Entry.COLUMN_NAME_LINK,
FeedContract.Entry.COLUMN_NAME_PUBLISHED
};
// Column indexes. The index of a column in the Cursor is the same as its relative position in
// the projection.
/** Column index for _ID */
private static final int COLUMN_ID = 0;
/** Column index for title */
private static final int COLUMN_TITLE = 1;
/** Column index for link */
private static final int COLUMN_URL_STRING = 2;
/** Column index for published */
private static final int COLUMN_PUBLISHED = 3;
/**
* List of Cursor columns to read from when preparing an adapter to populate the ListView.
*/
private static final String[] FROM_COLUMNS = new String[]{
FeedContract.Entry.COLUMN_NAME_TITLE,
FeedContract.Entry.COLUMN_NAME_PUBLISHED
};
/**
* List of Views which will be populated by Cursor data.
*/
private static final int[] TO_FIELDS = new int[]{
android.R.id.text1,
android.R.id.text2};
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public EntryListFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
/**
* Create SyncAccount at launch, if needed.
*
* This will create a new account with the system for our application, register our
* {@link SyncService} with it, and establish a sync schedule.
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Create account, if needed
SyncUtils.CreateSyncAccount(activity);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAdapter = new SimpleCursorAdapter(
getActivity(), // Current context
android.R.layout.simple_list_item_activated_2, // Layout for individual rows
null, // Cursor
FROM_COLUMNS, // Cursor columns to use
TO_FIELDS, // Layout fields to use
0 // No flags
);
mAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Cursor cursor, int i) {
if (i == COLUMN_PUBLISHED) {
// Convert timestamp to human-readable date
Time t = new Time();
t.set(cursor.getLong(i));
((TextView) view).setText(t.format("%Y-%m-%d %H:%M"));
return true;
} else {
// Let SimpleCursorAdapter handle other fields automatically
return false;
}
}
});
setListAdapter(mAdapter);
setEmptyText(getText(R.string.loading));
getLoaderManager().initLoader(0, null, this);
}
@Override
public void onResume() {
super.onResume();
mSyncStatusObserver.onStatusChanged(0);
// Watch for sync state changes
final int mask = ContentResolver.SYNC_OBSERVER_TYPE_PENDING |
ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
mSyncObserverHandle = ContentResolver.addStatusChangeListener(mask, mSyncStatusObserver);
}
@Override
public void onPause() {
super.onPause();
if (mSyncObserverHandle != null) {
ContentResolver.removeStatusChangeListener(mSyncObserverHandle);
mSyncObserverHandle = null;
}
}
/**
* Query the content provider for data.
*
* Loaders do queries in a background thread. They also provide a ContentObserver that is
* triggered when data in the content provider changes. When the sync adapter updates the
* content provider, the ContentObserver responds by resetting the loader and then reloading
* it.
*/
@Override
public Loader