SwipeRefreshLayout не обновляет данные из базы данных SQLite на временной шкале - PullRequest
0 голосов
/ 25 октября 2019

Я новичок в разработке Android и работаю над написанием клиента Twitter, и я пытаюсь реализовать pull для обновления, чтобы обновить временную шкалу пользователей.

В настоящее время временная шкала обновляется с использованием обработчика, который каждый раз выбирает новые твиты. 30 секунд внутри службы, но добавление pull для обновления было бы улучшением.

Я потратил некоторое время на изучение того, как это сделать, и использование метода notifyDataSetChanged на моем адаптере, кажется, путь, ноЯ не могу заставить его работать.

Я также пытался использовать GetTweets AsyncTask из класса обслуживания, но все еще не повезло.

Я удалил некоторые определения методов и clickListeners из кода, чтобы попробовать исделать это немного легче читать здесь.

Я уверен, что упускаю что-то простое с этим, но я не знаю, с чего начать. Заранее благодарим за любую помощь!

MainAcitivity.java



public class MainActivity extends AppCompatActivity implements OnClickListener {
    //Variable declarations
    //Developer keys provided by Twitter through a developer account
    //Developer account key for this app
    public final static String TWIT_KEY = "xxxxxxxxxx";
    //Developer key secret for the app
    public final static String TWIT_SECRET = "xxxxxxxxxx";
    //Application callback URL
    public final static String TWIT_URL = "xxxxxxxxxx://";
    //Twitter instance used with Twitter4j library to provide functionality
    private Twitter timelineTwitter;
    //Request token for accessing user account
    private RequestToken requestToken;
    //Shared preferences to store user details
    private SharedPreferences mainActivityPrefs;
    //Main view for the home timeline_layout, displays Tweets in chronological order
    private ListView homeTimeline;
    //SQLite database of tweets posted by users the application user follows and tweet they post
    private SQLiteDatabase timelineDB;
    //Cursor used for handling the SQLite database
    private Cursor timelineCursor;
    //Adapter class used to map data from the database and display to the user through the timeline_layout_layout.xml layout
    private TimelineAdapter timelineAdapter;
    //Uri to verify the application and user
    Uri twitURI;
    //Log tag used in error catching
    private String LOG_TAG = "MainActivity";
    //Broadcast receiver to receive updates when new tweets are available for the user's timeline_layout
    private BroadcastReceiver statusReceiver;
    //Application context set in onCreate method when activity starts
    Context mainActivityContext;

    private SwipeRefreshLayout timelineSwipeRefreshLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Assigning the application's context to the mainActivityContext variable
        mainActivityContext = this;
        //Checks the device has Biometrics enabled. Method declared in MainActivity
        checkBiometricSupport();
        //Get and set the shared preferences for the application
        mainActivityPrefs = getSharedPreferences("sharedPrefs", 0);
        //Check if user is already signed in and shared preferences are available
        if (mainActivityPrefs.getString("user_token", null) == null) {
            //No shared preferences stored so prompt the user to sign in. Display login_layoutxml
            setContentView(R.layout.login_layout);
            //Set up sign in button and listener ready for user to click
            Button signIn = findViewById(R.id.login);
            signIn.setOnClickListener(this);
            //Configure and build an instance of Twitter for use in the MainActivity Class
            ConfigurationBuilder cb = new ConfigurationBuilder();
            cb.setDebugEnabled(true)
                    .setOAuthConsumerKey(TWIT_KEY)
                    .setOAuthConsumerSecret(TWIT_SECRET);
            timelineTwitter = new TwitterFactory(cb.build()).getInstance();
            //Try to get a request token for session in background using AsyncTask to avoid network exception on main thread
            //Class declared in MainActivity
            CheckRequestToken checkRequestToken = new CheckRequestToken();
            checkRequestToken.execute();
        } else {
            //Else if shared preferences contain user data and access tokens set up the timeline_layout and display timeline_layout.xml
            //Method declared in MainActivity
            setupTimeline();
        }//End of if-else statement

    }//End of onCreate definition

    //Create options menu in top right of timeline_layout.xml containing 'Information' and 'Sign out' options
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menubar, menu);
        return true;
    }//End of onCreateOptionsMenu definition

    @Override
    public boolean onOptionsItemSelected(MenuItem item){
        switch(item.getItemId()){
            case R.id.information:
                //Create an AlertDialog to display the application's information. Inflates information_alertdialog.xml over timeline_layout.xml
                AlertDialog.Builder information = new AlertDialog.Builder(mainActivityContext,R.style.alertDialog);
                LayoutInflater inflater = (LayoutInflater)mainActivityContext.getSystemService(LAYOUT_INFLATER_SERVICE);
                View infoPopup = null;
                if (inflater != null) {
                    infoPopup = inflater.inflate(R.layout.information_alertdialog, null);
                }
                //Sets the layouts items to values before displaying AlertDialog to the user
                assert infoPopup != null;
                TextView title = infoPopup.findViewById(R.id.infoHeading);
                title.setText(R.string.information);
                TextView purposeMessage = infoPopup.findViewById(R.id.purposeMessage);
                purposeMessage.setText(R.string.applicationPurpose);
                TextView infoMessage = infoPopup.findViewById(R.id.infoMessage);
                infoMessage.setText(R.string.applicationInformation);
                information.setNeutralButton("OK",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                            }
                        });
                information.setView(infoPopup);
                information.show();
                //Return to activity
                return true;
            case R.id.signout:
                //Restart activity and erase cookies for sign in
                //Method declared in MainActivity
                logout(mainActivityContext);
                return true;
        }
        return super.onOptionsItemSelected(item);
    }//End of onOptionsItemSelected definition

    // onNewIntent fires when user returns from Twitter authentication Web page after sign in
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        //get the retrieved data from sign in with OAuth
        twitURI = intent.getData();
        //make sure the url is correct
        if(twitURI!=null && twitURI.toString().startsWith(TWIT_URL))
        {
            //Method to set shared preferences using data returned after sign in using OAuth
            VerifyBioAuth verifyBioAuth = new VerifyBioAuth();
            verifyBioAuth.execute();
        }
    }//End of onNewIntent definition

    //Click listener handles sign in and create_tweet button presses
    public void onClick(View v) {
        //find view
        switch(v.getId()) {
            //'Login' button pressed on login_layout.xml
            case R.id.login:
                //Launch a browser an take user to twitter authentication web page to allow app access to their twitter account
                String authURL = requestToken.getAuthenticationURL();
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authURL));
                intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
                startActivity(intent);
                break;
            //User has pressed create_tweet button in timeline_layout.xml
            case R.id.tweetButton:
                //launch TweetClass.class activity for user to type tweet and post it
                startActivity(new Intent(this, TweetClass.class));
                break;
            case R.id.messageButton:
                //Takes the user to their Inbox. Launches InboxActivity.class
                startActivity(new Intent(this, InboxActivity.class));
                break;
            default:
                break;
        }//End of switch statement
    }//End of onClick definition

    //Class to implement BroadcastReceiver to receiver new updates to users timeline
    class TwitterUpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //Sets the number of tweets the SQLite database can store to 100
            int rowLimit = 100;
            //Deletes old tweets which will be put into rows above 100 when new tweets are received
            if(DatabaseUtils.queryNumEntries(timelineDB, "home")>rowLimit) {
                String deleteQuery = "DELETE FROM home WHERE "+ BaseColumns._ID+" NOT IN " +
                        "(SELECT "+BaseColumns._ID+" FROM home ORDER BY "+"update_time DESC " +
                        "limit "+rowLimit+")";
                timelineDB.execSQL(deleteQuery);
            }
            //Gets new tweets and uses TimelineAdapter class to map them from the database to the timeline_layout.xml view
            timelineCursor = timelineDB.query("home", null, null, null, null, null, "update_time DESC");
            startManagingCursor(timelineCursor);
            timelineAdapter = new TimelineAdapter(context, timelineCursor);
            homeTimeline.setAdapter(timelineAdapter);
        }//End of onReceive
    }//End of TwitterUpdateReceiver definition

    //Stops the TimelineService class retrieving updates from Twitter and unregisters statusReceiver BroadcastReceiver
    @Override
    public void onDestroy() {
        super.onDestroy();
        try {
            //stop the updater Service
            stopService(new Intent(this, TimelineService.class));
            //remove receiver register
            unregisterReceiver(statusReceiver);
        }
        catch(Exception se) { Log.e(LOG_TAG, "unable to stop Service or receiver"); }
    }//End of onDestroy definition

     //setupTimeline displays the user's main home Twitter using the timeline_layout.xml
    private void setupTimeline() {
        setContentView(R.layout.timeline_layout); //Sets the timeline_layout layout style using timeline_layout.xmlout.xml
        //setup onClickListener for tweetButton. Launches TweetClass.class activity for user to type tweet and post it
        ImageButton tweetClicker = findViewById(R.id.tweetButton);
        tweetClicker.setOnClickListener(this);
        //setup onClickListener for messageButton. Launches InboxActivity.class activity for user to view their inbox of direct messages
        ImageButton messageClicker = findViewById(R.id.messageButton);
        messageClicker.setOnClickListener(this);

        //Try and set up the users timeline and get instances of other classes used in the process
        try {

            //Set timelineSwipeRefreshLayout and setOnRefreshListener. Display Toast notification to user that timeline
            //has been refreshed and notify timelineAdapter the data has changed
            timelineSwipeRefreshLayout = findViewById(R.id.timelineSwipeLayout);
            timelineSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    Toast.makeText(mainActivityContext, "Timeline refreshed", Toast.LENGTH_SHORT).show();
                    timelineAdapter.notifyDataSetChanged();
                    final Handler handler = new Handler();
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            if(timelineSwipeRefreshLayout.isRefreshing()) {
                                timelineSwipeRefreshLayout.setRefreshing(false);
                            }
                        }
                    }, 1000);
                }//End of onRefresh()
            });//End of setOnRefreshListener

            //get reference to the list view item of timeline_layout which shows tweet.xml layout for individual tweets
            homeTimeline = findViewById(R.id.timelineList);
            //Database helper for tweets. Gets tweet data ready to be displayed
            TimelineDataHelper timelineHelper = new TimelineDataHelper(this);
            //Gets the database of stored tweets
            timelineDB = timelineHelper.getReadableDatabase();
            //Query the database sorting the tweets in order of newest to oldest
            timelineCursor = timelineDB.query
                    ("home", null, null, null, null, null, "update_time DESC");
            //Use a cursor to manage the tweet updates
            startManagingCursor(timelineCursor);
            //Get an instance of the TimelineAdapter class to format data from database to display to the user
            timelineAdapter = new TimelineAdapter(this, timelineCursor);
            //Populates the timeline_layout.xml ListView item with retrieved Tweets. Each list item uses the tweet.xml layout
            homeTimeline.setAdapter(timelineAdapter);
            //Get an instance of the receiver to send notification when new tweets are available
            statusReceiver = new TwitterUpdateReceiver();
            //Register a receiver to receive the notifications of newly available tweets
            registerReceiver(statusReceiver, new IntentFilter("TWITTER_UPDATES"));
            //Start the service which retrieves the tweets from the users Twitter account
            this.getApplicationContext().startService(new Intent(this.getApplicationContext(), TimelineService.class));
        }
        catch(Exception te) { Log.e(LOG_TAG, "Failed to fetch timeline_layout: " + te.getMessage()); }
    }//End of setupTimeline method

TimelineAdapter.java


public class TimelineAdapter extends SimpleCursorAdapter {
    //twitter developer key
    private final static String TWIT_KEY = "xxxxxxxxxxs";
    //twitter developer secret
    private final static String TWIT_SECRET = "xxxxxxxxxx";
    //strings representing database column names to map to views
    private static final String[] from = { "update_text", "user_screen",
            "update_time", "user_img" };
    //view item IDs for mapping database record values to
    private static final int[] to = { R.id.updateText, R.id.userScreen,
            R.id.updateTime, R.id.userImg };
    //Context passed by constructor
    private Context adapterContext;
    //Instance of Twitter for use in the adapter
    private Twitter adapterTwitter;
    private String applicationUser;
    private Context mContext;

        //constructor sets up adapter, passing 'from' data and 'to' views
    TimelineAdapter(Context context, Cursor c) {
        super(context, R.layout.tweet, c, from, to);
        adapterContext = context;
        //Shared preferences for application
        SharedPreferences sharedPrefs = context.getSharedPreferences("sharedPrefs", 0);
        String userToken = sharedPrefs.getString("user_token", null);
        String userSecret = sharedPrefs.getString("user_secret", null);
        //create new Twitter configuration for use by adapter
        Configuration twitConf = new ConfigurationBuilder()
                .setOAuthConsumerKey(TWIT_KEY)
                .setOAuthConsumerSecret(TWIT_SECRET)
                .setOAuthAccessToken(userToken)
                .setOAuthAccessTokenSecret(userSecret)
                .setTweetModeExtended(true)
                .build();
        //create Twitter instance for retweeting
        adapterTwitter = new TwitterFactory(twitConf).getInstance();
        applicationUser = sharedPrefs.getString("twitter_user",null);
        mContext = context;
    }//End of constructor

//Bind the data to the visible views
    @Override
    public void bindView(View row, Context context, Cursor cursor) {
        super.bindView(row, context, cursor);
        //Try and get profileImage from URL stored in SQLite database
        try {
            URL profileURL =
                    new URL(cursor.getString(cursor.getColumnIndex("user_img")));
            ImageView profPic = row.findViewById(R.id.userImg);
            Picasso.get().load(profileURL.toString()).into(profPic);
        }catch(Exception te) { //Tag for logged events
            String LOG_TAG = "TimelineAdapter";
            Log.e(LOG_TAG, "" + te.getMessage()); }
        //get the tweet time
        long createdAt = cursor.getLong(cursor.getColumnIndex("update_time"));
        //get the tweet time view
        TextView textCreatedAt = row.findViewById(R.id.updateTime);
        //adjust the way the time is displayed to make it human-readable
        textCreatedAt.setText(DateUtils.getRelativeTimeSpanString(createdAt));
        //get the status ID
        long statusID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
        //get the user name
        String statusName = cursor.getString(cursor.getColumnIndex("user_screen"));
        //create a UserData object to store these
        UserData tweetData = new UserData(statusID, statusName);
        //set the status data object as tag for both retweet and reply and flag tweet buttons
        row.findViewById(R.id.retweet).setTag(tweetData);
        row.findViewById(R.id.reply).setTag(tweetData);
        row.findViewById(R.id.tweetMenu).setTag(tweetData);
        //setup onclick listeners for buttons in tweet.xml
        row.findViewById(R.id.retweet).setOnClickListener(tweetListener);
        row.findViewById(R.id.reply).setOnClickListener(tweetListener);
        row.findViewById(R.id.tweetMenu).setOnClickListener(tweetListener);
        //setup  onclick for the user screen name within the tweet.xml
        row.findViewById(R.id.userScreen).setOnClickListener(tweetListener);

    }//End of bindView


}//End of class definition

TimelineService.java


public class TimelineService extends Service {
    //twitter authentication key
    public final static String TWIT_KEY = "xxxxxxxxxx";
    //twitter secret
    public final static String TWIT_SECRET = "xxxxxxxxxx";
    SharedPreferences timelineServicePrefs;
    //twitter object
    private Twitter timelineTwitter;
    //timeline_layout database
    private SQLiteDatabase timelineDB;
    //handler for updater
    public Handler timelineHandler;
    //updater thread object
    private TimelineUpdater timelineUpdater;
    private boolean statusChanges;

    @Override
    public void onCreate(){
        super.onCreate();
        //Setting up the class
        //get preferences
        //shared preferences for user details
        timelineServicePrefs = getSharedPreferences("sharedPrefs", 0);
        //get user preferences
        String userToken = timelineServicePrefs.getString("user_token", null);
        String userSecret = timelineServicePrefs.getString("user_secret", null);
        //database helper object
        TimelineDataHelper timelineHelper = new TimelineDataHelper(this);
        //get the database
        timelineDB = timelineHelper.getWritableDatabase();
        //create new configuration
        Configuration twitConf = new ConfigurationBuilder()
                .setOAuthConsumerKey(TWIT_KEY)
                .setOAuthConsumerSecret(TWIT_SECRET)
                .setOAuthAccessToken(userToken)
                .setOAuthAccessTokenSecret(userSecret)
                .build();
        //instantiate new twitter
        timelineTwitter = new TwitterFactory(twitConf).getInstance();
    }//End of onCreate

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }//End of onBind

    // TimelineUpdater class implements the runnable interface
    class TimelineUpdater implements Runnable{
        //run method
        public void run() {
            //check for updates - assume none
            statusChanges = false;
            //get tweets in AsyncTask to avoid network exception on main thread
            GetTweetsTask getTweetsTask = new GetTweetsTask();
            getTweetsTask.execute();
            //delay fetching new updates
            timelineHandler.postDelayed(this, 30 * 1000);//Fetch new tweets every 30 seconds
        }
    }//End of TimelineUpdater

    class GetTweetsTask extends AsyncTask<Void, Void, List<Status>> {

        @Override
        protected List<twitter4j.Status> doInBackground(Void... voids) {
            try {
                //fetch timeline_layout
                //retrieve the new home timeline_layout tweets as a list
                List<twitter4j.Status> homeTimeline = timelineTwitter.getHomeTimeline();
                //iterate through new status updates
                for (twitter4j.Status statusUpdate : homeTimeline)
                {
                    //call the getValues method of the data helper class, passing the new updates
                    ContentValues timelineValues = TimelineDataHelper.getValues(statusUpdate);
                    //if the database already contains the updates they will not be inserted
                    timelineDB.insertWithOnConflict("home", null, timelineValues,SQLiteDatabase.CONFLICT_REPLACE);
                    //confirm we have new updates
                    statusChanges = true;
                    if (statusChanges)
                    {
                        //this should be received in the main timeline_layout class
                        sendBroadcast(new Intent("TWITTER_UPDATES"));
                    }
                }
            }
            catch (Exception te) { //debugging tag
                String LOG_TAG = "TimelineService";
                Log.e(LOG_TAG, "Exception: " + te); }
            return null;
        }

        @Override
        protected void onPostExecute(List<twitter4j.Status> homeTimeline) {
            super.onPostExecute(homeTimeline);
        }

    }//End of GetTweetsTask

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        //get handler
        timelineHandler = new Handler();
        //create an instance of the updater class
        timelineUpdater = new TimelineUpdater();
        //add to run queue
        timelineHandler.post(timelineUpdater);
        //return sticky
        return START_STICKY;
    }//End of onStartCommand

    @Override
    public void onDestroy() {
        super.onDestroy();
        //stop the updating
        this.timelineHandler.removeCallbacks(timelineUpdater);
     }//End of onDestroy
}//End of class definition

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...