Home | History | Annotate | Download | only in agendadata
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.wearable.agendadata;
     18 
     19 import static com.example.android.wearable.agendadata.Constants.TAG;
     20 
     21 import android.Manifest;
     22 import android.content.Intent;
     23 import android.content.IntentSender;
     24 import android.content.pm.PackageManager;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.support.annotation.NonNull;
     28 import android.support.design.widget.Snackbar;
     29 import android.support.v4.app.ActivityCompat;
     30 import android.support.v7.app.AppCompatActivity;
     31 import android.util.Log;
     32 import android.view.View;
     33 import android.widget.ScrollView;
     34 import android.widget.TextView;
     35 
     36 import com.google.android.gms.common.ConnectionResult;
     37 import com.google.android.gms.common.api.GoogleApiClient;
     38 import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
     39 import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
     40 import com.google.android.gms.common.api.ResultCallback;
     41 import com.google.android.gms.wearable.DataApi;
     42 import com.google.android.gms.wearable.DataItem;
     43 import com.google.android.gms.wearable.DataItemBuffer;
     44 import com.google.android.gms.wearable.Wearable;
     45 
     46 /**
     47  * Syncs or deletes calendar events (event time, description, and background image) to your
     48  * Wearable via the Wearable DataApi at the click of a button. Includes code to handle dynamic M+
     49  * permissions as well.
     50  */
     51 public class MainActivity extends AppCompatActivity implements
     52         ConnectionCallbacks,
     53         OnConnectionFailedListener,
     54         ActivityCompat.OnRequestPermissionsResultCallback {
     55 
     56     /* Request code for launching the Intent to resolve Google Play services errors. */
     57     private static final int REQUEST_RESOLVE_ERROR = 1000;
     58 
     59     /* Id to identify calendar and contact permissions request. */
     60     private static final int REQUEST_CALENDAR_AND_CONTACTS = 0;
     61 
     62 
     63     private GoogleApiClient mGoogleApiClient;
     64     private boolean mResolvingError = false;
     65 
     66     private View mLayout;
     67 
     68     private TextView mLogTextView;
     69     private ScrollView mScroller;
     70 
     71     @Override
     72     protected void onCreate(Bundle savedInstanceState) {
     73         super.onCreate(savedInstanceState);
     74 
     75         setContentView(R.layout.main);
     76         mLayout = findViewById(R.id.main_layout);
     77 
     78         mLogTextView = (TextView) findViewById(R.id.log);
     79         mScroller = (ScrollView) findViewById(R.id.scroller);
     80 
     81         mGoogleApiClient = new GoogleApiClient.Builder(this)
     82                 .addApi(Wearable.API)
     83                 .addConnectionCallbacks(this)
     84                 .addOnConnectionFailedListener(this)
     85                 .build();
     86     }
     87 
     88     @Override
     89     protected void onStart() {
     90         super.onStart();
     91         if (!mResolvingError) {
     92             mGoogleApiClient.connect();
     93         }
     94     }
     95 
     96     @Override
     97     protected void onStop() {
     98         if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
     99             mGoogleApiClient.disconnect();
    100         }
    101 
    102         super.onStop();
    103     }
    104 
    105     public void onGetEventsClicked(View view) {
    106 
    107         Log.i(TAG, "onGetEventsClicked(): Checking permission.");
    108 
    109         // BEGIN_INCLUDE(calendar_and_contact_permissions)
    110         // Check if the Calendar permission is already available.
    111         boolean calendarApproved =
    112                 ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR)
    113                         == PackageManager.PERMISSION_GRANTED;
    114 
    115         boolean contactsApproved =
    116                 ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
    117                         == PackageManager.PERMISSION_GRANTED;
    118 
    119         if (!calendarApproved || !contactsApproved) {
    120             // Calendar and/or Contact permissions have not been granted.
    121            requestCalendarAndContactPermissions();
    122 
    123         } else {
    124             // Calendar permissions is already available, start service
    125             Log.i(TAG, "Permissions already granted. Starting service.");
    126             pushCalendarToWear();
    127         }
    128         // END_INCLUDE(calendar_and_contact_permissions)
    129 
    130     }
    131 
    132     /*
    133      * Requests Calendar and Contact permissions.
    134      * If the permission has been denied previously, a SnackBar will prompt the user to grant the
    135      * permission, otherwise it is requested directly.
    136      */
    137     private void requestCalendarAndContactPermissions() {
    138         Log.i(TAG, "CALENDAR permission has NOT been granted. Requesting permission.");
    139 
    140         // BEGIN_INCLUDE(calendar_and_contact_permissions_request)
    141 
    142         boolean showCalendarPermissionRationale =
    143                 ActivityCompat.shouldShowRequestPermissionRationale(this,
    144                         Manifest.permission.READ_CALENDAR);
    145         boolean showContactsPermissionRationale =
    146                 ActivityCompat.shouldShowRequestPermissionRationale(this,
    147                         Manifest.permission.READ_CONTACTS);
    148 
    149         if (showCalendarPermissionRationale || showContactsPermissionRationale) {
    150             /*
    151              * Provide an additional rationale to the user if the permission was not granted and
    152              * the user would benefit from additional context for the use of the permission. For
    153              * example, if the user has previously denied the permission.
    154              */
    155             Log.i(TAG, "Display calendar & contact permissions rationale for additional context.");
    156 
    157             Snackbar.make(mLayout, R.string.permissions_rationale,
    158                     Snackbar.LENGTH_INDEFINITE)
    159                     .setAction(R.string.ok, new View.OnClickListener() {
    160                         @Override
    161                         public void onClick(View view) {
    162                             ActivityCompat.requestPermissions(MainActivity.this,
    163                                     new String[] {
    164                                             Manifest.permission.READ_CALENDAR,
    165                                             Manifest.permission.READ_CONTACTS},
    166                                     REQUEST_CALENDAR_AND_CONTACTS);
    167                         }
    168                     })
    169                     .show();
    170 
    171 
    172         } else {
    173 
    174             // Calendar/Contact permissions have not been granted yet. Request it directly.
    175             ActivityCompat.requestPermissions(
    176                     this,
    177                     new String[]{
    178                             Manifest.permission.READ_CALENDAR,
    179                             Manifest.permission.READ_CONTACTS
    180                     },
    181                     REQUEST_CALENDAR_AND_CONTACTS);
    182         }
    183         // END_INCLUDE(calendar_and_contact_permissions_request)
    184     }
    185 
    186     private void pushCalendarToWear() {
    187         startService(new Intent(this, CalendarQueryService.class));
    188     }
    189 
    190     public void onDeleteEventsClicked(View view) {
    191         if (mGoogleApiClient.isConnected()) {
    192             Wearable.DataApi.getDataItems(mGoogleApiClient)
    193                     .setResultCallback(new ResultCallback<DataItemBuffer>() {
    194                         @Override
    195                         public void onResult(DataItemBuffer result) {
    196                             try {
    197                                 if (result.getStatus().isSuccess()) {
    198                                     deleteDataItems(result);
    199                                 } else {
    200                                     if (Log.isLoggable(TAG, Log.DEBUG)) {
    201                                         Log.d(TAG, "onDeleteEventsClicked(): failed to get Data "
    202                                                 + "Items");
    203                                     }
    204                                 }
    205                             } finally {
    206                                 result.release();
    207                             }
    208                         }
    209                     });
    210         } else {
    211             Log.e(TAG, "Failed to delete data items"
    212                     + " - Client disconnected from Google Play Services");
    213         }
    214     }
    215 
    216     private void deleteDataItems(final DataItemBuffer dataItemList) {
    217         if (mGoogleApiClient.isConnected()) {
    218             for (final DataItem dataItem : dataItemList) {
    219                 final Uri dataItemUri = dataItem.getUri();
    220                 /*
    221                  * In a real calendar application, this might delete the corresponding calendar
    222                  * events from the calendar data provider. However, we simply delete the DataItem,
    223                  * but leave the phone's calendar data intact for this simple sample.
    224                  */
    225                 Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
    226                         .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
    227                             @Override
    228                             public void onResult(DataApi.DeleteDataItemsResult deleteResult) {
    229                                 if (deleteResult.getStatus().isSuccess()) {
    230                                     appendLog("Successfully deleted data item: " + dataItemUri);
    231                                 } else {
    232                                     appendLog("Failed to delete data item:" + dataItemUri);
    233                                 }
    234                             }
    235                         });
    236             }
    237         } else {
    238             Log.e(TAG, "Failed to delete data items"
    239                      + " - Client disconnected from Google Play Services");
    240         }
    241     }
    242 
    243     @Override
    244     public void onConnected(Bundle connectionHint) {
    245         if (Log.isLoggable(TAG, Log.DEBUG)) {
    246             Log.d(TAG, "Connected to Google Api Service.");
    247         }
    248         mResolvingError = false;
    249     }
    250 
    251     @Override
    252     public void onConnectionSuspended(int cause) {
    253         if (Log.isLoggable(TAG, Log.DEBUG)) {
    254             Log.d(TAG, "onConnectionSuspended(): Cause id: " + cause);
    255         }
    256     }
    257 
    258     @Override
    259     public void onConnectionFailed(ConnectionResult result) {
    260         if (Log.isLoggable(TAG, Log.DEBUG)) {
    261             Log.d(TAG, "Disconnected from Google Api Service");
    262         }
    263 
    264         if (mResolvingError) {
    265             // Already attempting to resolve an error.
    266             return;
    267         } else if (result.hasResolution()) {
    268             try {
    269                 mResolvingError = true;
    270                 result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
    271             } catch (IntentSender.SendIntentException e) {
    272                 // There was an error with the resolution intent. Try again.
    273                 mResolvingError = false;
    274                 mGoogleApiClient.connect();
    275             }
    276         } else {
    277             mResolvingError = false;
    278         }
    279     }
    280 
    281     @Override
    282     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    283         super.onActivityResult(requestCode, resultCode, data);
    284         if (Log.isLoggable(TAG, Log.DEBUG)) {
    285             Log.d(TAG, "onActivityResult request/result codes: " + requestCode + "/" + resultCode);
    286         }
    287 
    288         if (requestCode == REQUEST_RESOLVE_ERROR) {
    289             mResolvingError = false;
    290             if (resultCode == RESULT_OK) {
    291                 // Make sure the app is not already connected or attempting to connect
    292                 if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) {
    293                     mGoogleApiClient.connect();
    294                 }
    295             }
    296         }
    297     }
    298 
    299     /**
    300      * Callback received when a permissions request has been completed.
    301      */
    302     @Override
    303     public void onRequestPermissionsResult(
    304             int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    305 
    306         if (Log.isLoggable(TAG, Log.DEBUG)) {
    307             Log.d(TAG, "onRequestPermissionsResult(): " + permissions);
    308         }
    309 
    310         if (requestCode == REQUEST_CALENDAR_AND_CONTACTS) {
    311             // BEGIN_INCLUDE(permissions_result)
    312             // Received permission result for calendar permission.
    313             Log.i(TAG, "Received response for Calendar permission request.");
    314 
    315             // Check if all required permissions have been granted.
    316             if ((grantResults.length == 2)
    317                     && (grantResults[0] == PackageManager.PERMISSION_GRANTED)
    318                     && (grantResults[1] == PackageManager.PERMISSION_GRANTED)) {
    319                 // Calendar/Contact permissions have been granted, pull all calendar events
    320                 Log.i(TAG, "All permission has now been granted. Showing preview.");
    321                 Snackbar.make(mLayout, R.string.permisions_granted, Snackbar.LENGTH_SHORT).show();
    322 
    323                 pushCalendarToWear();
    324 
    325             } else {
    326                 Log.i(TAG, "CALENDAR and/or CONTACT permissions were NOT granted.");
    327                 Snackbar.make(mLayout, R.string.permissions_denied, Snackbar.LENGTH_SHORT).show();
    328             }
    329             // END_INCLUDE(permissions_result)
    330 
    331         } else {
    332             super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    333         }
    334     }
    335 
    336     private void appendLog(final String s) {
    337         mLogTextView.post(new Runnable() {
    338             @Override
    339             public void run() {
    340                 mLogTextView.append(s);
    341                 mLogTextView.append("\n");
    342                 mScroller.fullScroll(View.FOCUS_DOWN);
    343             }
    344         });
    345     }
    346 }