<commit_before>package com.kennyc.dashweather.services

import android.Manifest
import android.content.*
import android.content.pm.PackageManager
import android.location.Geocoder
import android.net.ConnectivityManager
import android.os.Build
import android.os.PowerManager
import android.preference.PreferenceManager
import android.support.annotation.Nullable
import android.support.v4.content.ContextCompat
import android.text.format.DateUtils
import android.util.Log
import com.google.android.apps.dashclock.api.DashClockExtension
import com.google.android.apps.dashclock.api.ExtensionData
import com.google.android.gms.location.LocationServices
import com.kennyc.dashweather.R
import com.kennyc.dashweather.SettingsActivity
import com.kennyc.dashweather.SettingsFragment
import com.kennyc.dashweather.api.ApiClient
import com.kennyc.dashweather.api.WeatherResult
import com.kennyc.dashweather.models.DailyWeatherModel
import com.kennyc.dashweather.models.WeatherModel
import retrofit2.Response
import java.util.*
import kotlin.concurrent.thread


/**
 * Created by Kenny-PC on 9/22/2017.
 */
class DarkSkyDashExtension : DashClockExtension() {
    companion object {
        const val KEY_LAST_UPDATED = "com.kennyc.dashweather.LAST_UPDATE"
        const val TAG = "DarkSkyDashExtension"
        const val INTENT_ACTION = "com.kennyc.dashweather.REFRESH"

        fun sendBroadcast(context: Context) {
            context.sendBroadcast(Intent(INTENT_ACTION))
        }
    }

    private var broadcastReceiver: BroadcastReceiver? = null

    override fun onInitialize(isReconnect: Boolean) {
        Log.v(TAG, "onInitialize")
        super.onInitialize(isReconnect)
        if (broadcastReceiver != null) {
            try {
                unregisterReceiver(broadcastReceiver)
            } catch (ex: Exception) {
                Log.e(TAG, "Unable to unregister receiver", ex)
            }
        }

        broadcastReceiver = DashClockReceiver()
        registerReceiver(broadcastReceiver, IntentFilter(INTENT_ACTION))
    }

    override fun onDestroy() {
        Log.v(TAG, "onDestroy")
        super.onDestroy()
        if (broadcastReceiver != null) {
            try {
                unregisterReceiver(broadcastReceiver)
            } catch (ex: Exception) {
                Log.e(TAG, "Unable to unregister receiver:", ex)
            }
        }
    }

    /**
     * Called when the DashClock app process is requesting that the extension provide updated
     * information to show to the user. Implementations can choose to do nothing, or more commonly,
     * provide an update using the [.publishUpdate] method. Note that doing
     * nothing doesn't clear existing data. To clear any existing data, call
     * [.publishUpdate] with `null` data.
     *
     * @param reason The reason for the update. See [.UPDATE_REASON_PERIODIC] and related
     * constants for more details.
     */
    override fun onUpdateData(reason: Int) {
        Log.v(TAG, "onUpdateData - reason: " + reason)
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)

        if (reason == UPDATE_REASON_MANUAL || shouldUpdate(sharedPreferences)) {
            sharedPreferences.edit().putLong(KEY_LAST_UPDATED, System.currentTimeMillis()).apply()
            val coarsePermission = ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.ACCESS_COARSE_LOCATION)
            val finePermission = ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.ACCESS_FINE_LOCATION)

            if (coarsePermission == PackageManager.PERMISSION_GRANTED || finePermission == PackageManager.PERMISSION_GRANTED) {
                val fusedLocation = LocationServices.getFusedLocationProviderClient(applicationContext)
                fusedLocation.lastLocation.addOnSuccessListener { location ->
                    if (location != null) {
                        onLocationReceived(location.latitude, location.longitude)
                    } else {
                        onLocationFailed(NullPointerException("No location received"))
                    }
                }.addOnFailureListener { exception ->
                    onLocationFailed(exception)
                }
            } else {
                onPermissionMissing()
            }
        }
    }

    private fun onLocationReceived(latitude: Double, longitude: Double) {
        val formattedLocation = String.format("%.4f,%.4f", latitude, longitude)
        Log.v(TAG, "Getting weather for " + formattedLocation)

        thread {
            val imperial = PreferenceManager.getDefaultSharedPreferences(applicationContext).getBoolean(getString(R.string.pref_key_use_imperial), true)
            val unit = if (imperial) "us" else "si"
            val weatherResult = ApiClient.darkSkyService.getForecast(formattedLocation, unit).execute()
            logApiResponse(weatherResult)
            onApiResponse(weatherResult.body(), imperial)
        }
    }

    private fun onApiResponse(weatherResult: WeatherResult?, usesImperial: Boolean) {
        val current = weatherResult?.currently
        val daily = weatherResult?.daily
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
        val currentTemp: String
        val iconDrawable: Int
        val currentCondition: String

        if (current != null) {
            val temp = Math.round(current.temperature)
            currentTemp = getString(if (usesImperial) R.string.temp_F else R.string.temp_C, temp)
            iconDrawable = current.getIconDrawable()
            currentCondition = current.summary
        } else {
            currentTemp = "???"
            iconDrawable = R.drawable.ic_weather_sunny_black_24dp
            currentCondition = "???"
        }

        val expandedBody = StringBuilder()
        val highLow = getHighLow(sharedPreferences, daily, usesImperial)
        if (highLow != null) expandedBody.append(highLow)

        val humidity = getHumidity(sharedPreferences, current)
        if (humidity != null) {
            if (expandedBody.isNotEmpty()) expandedBody.append("\n")
            expandedBody.append(humidity)
        }

        val uvIndex = getUVIndex(sharedPreferences, current)
        if (uvIndex != null) {
            if (expandedBody.isNotEmpty()) expandedBody.append("\n")
            expandedBody.append(uvIndex)
        }

        // TODO Better check this
        val location = getLastLocation(sharedPreferences, weatherResult!!.latitude, weatherResult!!.longitude)
        if (location != null) {
            if (expandedBody.isNotEmpty()) expandedBody.append("\n")
            expandedBody.append(location)
        }

        val body = expandedBody.toString()

        publishUpdate(ExtensionData()
                .visible(true)
                .clickIntent(Intent(INTENT_ACTION))
                .icon(iconDrawable)
                .status(currentTemp)
                .expandedTitle(currentTemp + " - " + currentCondition)
                .expandedBody(body))
    }

    private fun onLocationFailed(exception: Exception) {
        Log.e(TAG, "unable to get location", exception)
        publishUpdate(ExtensionData()
                .visible(true)
                .icon(R.drawable.ic_map_marker_off_black_24dp)
                .status(getString(R.string.error_no_location))
                .expandedTitle(getString(R.string.error_no_location))
                .expandedBody(getString(R.string.error_no_location_desc)))
    }

    private fun onPermissionMissing() {
        publishUpdate(ExtensionData()
                .visible(true)
                .icon(R.drawable.ic_map_marker_off_black_24dp)
                .status(getString(R.string.permission_title))
                .expandedTitle(getString(R.string.permission_title))
                .expandedBody(getString(R.string.permission_body))
                .clickIntent(SettingsActivity.createIntent(applicationContext, true)))
    }

    private fun logApiResponse(response: Response<WeatherResult>) {
        val headers = response.headers()
        val headerNames = headers.names()
        for (name in headerNames) Log.i(TAG, name + ": " + headers.get(name))
        Log.i(TAG, "Response Code: " + response.code())
    }

    private fun shouldUpdate(sharedPreferences: SharedPreferences): Boolean {
        // Check if data saver is on
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val connMgr = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

            if (connMgr.isActiveNetworkMetered && connMgr.restrictBackgroundStatus == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED) {
                Log.v(TAG, "Data saver enabled and on a metered network, skipping update")
                return false
            }
        }

        // Check if battery saver is enabled
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val powerManager = applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
            if (powerManager.isPowerSaveMode) {
                Log.v(TAG, "Battery saver enabled, skipping update")
                return false
            }
        }

        val currentTime = System.currentTimeMillis()
        val updateFrequency = sharedPreferences.getString(getString(R.string.pref_key_update_frequency), SettingsFragment.UPDATE_FREQUENCY_1_HOUR)
        val lastUpdate = sharedPreferences.getLong(KEY_LAST_UPDATED, 0)<commit_msg>Only log api response for debug builds<commit_after>package com.kennyc.dashweather.services

import android.Manifest
import android.content.*
import android.content.pm.PackageManager
import android.location.Geocoder
import android.net.ConnectivityManager
import android.os.Build
import android.os.PowerManager
import android.preference.PreferenceManager
import android.support.annotation.Nullable
import android.support.v4.content.ContextCompat
import android.text.format.DateUtils
import android.util.Log
import com.google.android.apps.dashclock.api.DashClockExtension
import com.google.android.apps.dashclock.api.ExtensionData
import com.google.android.gms.location.LocationServices
import com.kennyc.dashweather.BuildConfig
import com.kennyc.dashweather.R
import com.kennyc.dashweather.SettingsActivity
import com.kennyc.dashweather.SettingsFragment
import com.kennyc.dashweather.api.ApiClient
import com.kennyc.dashweather.api.WeatherResult
import com.kennyc.dashweather.models.DailyWeatherModel
import com.kennyc.dashweather.models.WeatherModel
import retrofit2.Response
import java.util.*
import kotlin.concurrent.thread


/**
 * Created by Kenny-PC on 9/22/2017.
 */
class DarkSkyDashExtension : DashClockExtension() {
    companion object {
        const val KEY_LAST_UPDATED = "com.kennyc.dashweather.LAST_UPDATE"
        const val TAG = "DarkSkyDashExtension"
        const val INTENT_ACTION = "com.kennyc.dashweather.REFRESH"

        fun sendBroadcast(context: Context) {
            context.sendBroadcast(Intent(INTENT_ACTION))
        }
    }

    private var broadcastReceiver: BroadcastReceiver? = null

    override fun onInitialize(isReconnect: Boolean) {
        Log.v(TAG, "onInitialize")
        super.onInitialize(isReconnect)
        if (broadcastReceiver != null) {
            try {
                unregisterReceiver(broadcastReceiver)
            } catch (ex: Exception) {
                Log.e(TAG, "Unable to unregister receiver", ex)
            }
        }

        broadcastReceiver = DashClockReceiver()
        registerReceiver(broadcastReceiver, IntentFilter(INTENT_ACTION))
    }

    override fun onDestroy() {
        Log.v(TAG, "onDestroy")
        super.onDestroy()
        if (broadcastReceiver != null) {
            try {
                unregisterReceiver(broadcastReceiver)
            } catch (ex: Exception) {
                Log.e(TAG, "Unable to unregister receiver:", ex)
            }
        }
    }

    /**
     * Called when the DashClock app process is requesting that the extension provide updated
     * information to show to the user. Implementations can choose to do nothing, or more commonly,
     * provide an update using the [.publishUpdate] method. Note that doing
     * nothing doesn't clear existing data. To clear any existing data, call
     * [.publishUpdate] with `null` data.
     *
     * @param reason The reason for the update. See [.UPDATE_REASON_PERIODIC] and related
     * constants for more details.
     */
    override fun onUpdateData(reason: Int) {
        Log.v(TAG, "onUpdateData - reason: " + reason)
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)

        if (reason == UPDATE_REASON_MANUAL || shouldUpdate(sharedPreferences)) {
            sharedPreferences.edit().putLong(KEY_LAST_UPDATED, System.currentTimeMillis()).apply()
            val coarsePermission = ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.ACCESS_COARSE_LOCATION)
            val finePermission = ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.ACCESS_FINE_LOCATION)

            if (coarsePermission == PackageManager.PERMISSION_GRANTED || finePermission == PackageManager.PERMISSION_GRANTED) {
                val fusedLocation = LocationServices.getFusedLocationProviderClient(applicationContext)
                fusedLocation.lastLocation.addOnSuccessListener { location ->
                    if (location != null) {
                        onLocationReceived(location.latitude, location.longitude)
                    } else {
                        onLocationFailed(NullPointerException("No location received"))
                    }
                }.addOnFailureListener { exception ->
                    onLocationFailed(exception)
                }
            } else {
                onPermissionMissing()
            }
        }
    }

    private fun onLocationReceived(latitude: Double, longitude: Double) {
        val formattedLocation = String.format("%.4f,%.4f", latitude, longitude)
        Log.v(TAG, "Getting weather for " + formattedLocation)

        thread {
            val imperial = PreferenceManager.getDefaultSharedPreferences(applicationContext).getBoolean(getString(R.string.pref_key_use_imperial), true)
            val unit = if (imperial) "us" else "si"
            val weatherResult = ApiClient.darkSkyService.getForecast(formattedLocation, unit).execute()
            logApiResponse(weatherResult)
            onApiResponse(weatherResult.body(), imperial)
        }
    }

    private fun onApiResponse(weatherResult: WeatherResult?, usesImperial: Boolean) {
        val current = weatherResult?.currently
        val daily = weatherResult?.daily
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
        val currentTemp: String
        val iconDrawable: Int
        val currentCondition: String

        if (current != null) {
            val temp = Math.round(current.temperature)
            currentTemp = getString(if (usesImperial) R.string.temp_F else R.string.temp_C, temp)
            iconDrawable = current.getIconDrawable()
            currentCondition = current.summary
        } else {
            currentTemp = "???"
            iconDrawable = R.drawable.ic_weather_sunny_black_24dp
            currentCondition = "???"
        }

        val expandedBody = StringBuilder()
        val highLow = getHighLow(sharedPreferences, daily, usesImperial)
        if (highLow != null) expandedBody.append(highLow)

        val humidity = getHumidity(sharedPreferences, current)
        if (humidity != null) {
            if (expandedBody.isNotEmpty()) expandedBody.append("\n")
            expandedBody.append(humidity)
        }

        val uvIndex = getUVIndex(sharedPreferences, current)
        if (uvIndex != null) {
            if (expandedBody.isNotEmpty()) expandedBody.append("\n")
            expandedBody.append(uvIndex)
        }

        // TODO Better check this
        val location = getLastLocation(sharedPreferences, weatherResult!!.latitude, weatherResult!!.longitude)
        if (location != null) {
            if (expandedBody.isNotEmpty()) expandedBody.append("\n")
            expandedBody.append(location)
        }

        val body = expandedBody.toString()

        publishUpdate(ExtensionData()
                .visible(true)
                .clickIntent(Intent(INTENT_ACTION))
                .icon(iconDrawable)
                .status(currentTemp)
                .expandedTitle(currentTemp + " - " + currentCondition)
                .expandedBody(body))
    }

    private fun onLocationFailed(exception: Exception) {
        Log.e(TAG, "unable to get location", exception)
        publishUpdate(ExtensionData()
                .visible(true)
                .icon(R.drawable.ic_map_marker_off_black_24dp)
                .status(getString(R.string.error_no_location))
                .expandedTitle(getString(R.string.error_no_location))
                .expandedBody(getString(R.string.error_no_location_desc)))
    }

    private fun onPermissionMissing() {
        publishUpdate(ExtensionData()
                .visible(true)
                .icon(R.drawable.ic_map_marker_off_black_24dp)
                .status(getString(R.string.permission_title))
                .expandedTitle(getString(R.string.permission_title))
                .expandedBody(getString(R.string.permission_body))
                .clickIntent(SettingsActivity.createIntent(applicationContext, true)))
    }

    private fun logApiResponse(response: Response<WeatherResult>) {
        if (BuildConfig.DEBUG) {
            val headers = response.headers()
            val headerNames = headers.names()
            for (name in headerNames) Log.i(TAG, name + ": " + headers.get(name))
            Log.i(TAG, "Response Code: " + response.code())
        }
    }

    private fun shouldUpdate(sharedPreferences: SharedPreferences): Boolean {
        // Check if data saver is on
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val connMgr = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

            if (connMgr.isActiveNetworkMetered && connMgr.restrictBackgroundStatus == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED) {
                Log.v(TAG, "Data saver enabled and on a metered network, skipping update")
                return false
            }
        }

        // Check if battery saver is enabled
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val powerManager = applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
            if (powerManager.isPowerSaveMode) {
                Log.v(TAG, "Battery saver enabled, skipping update")
                return false
            }
        }

        val currentTime = System.currentTimeMillis()
        val updateFrequency = sharedPreferences.getString(getString(R.string.pref_key_update_frequency), SettingsFragment.UPDATE_FREQUENCY_1_HOUR)
        val lastUpdate = sharedPreferences.getLong(KEY_LAST_UPDATED, 0)
{'fraction_non_alphanumeric': 0.08627328546154647, 'fraction_numerical': 0.001977312935789364, 'mean_word_length': 2.6323946323946323, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1090683', 'n_tokens_mistral': 5126, 'n_tokens_neox': 4923, 'n_words': 1021}