1 /* 2 * Copyright (C) 2017 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.downloadablefonts 18 19 import android.graphics.Typeface 20 import android.os.Bundle 21 import android.os.Handler 22 import android.os.HandlerThread 23 import android.support.design.widget.TextInputLayout 24 import android.support.v4.provider.FontRequest 25 import android.support.v4.provider.FontsContractCompat 26 import android.support.v4.util.ArraySet 27 import android.support.v7.app.AppCompatActivity 28 import android.text.Editable 29 import android.text.TextWatcher 30 import android.util.Log 31 import android.view.View 32 import android.widget.ArrayAdapter 33 import android.widget.AutoCompleteTextView 34 import android.widget.Button 35 import android.widget.CheckBox 36 import android.widget.ProgressBar 37 import android.widget.SeekBar 38 import android.widget.TextView 39 import android.widget.Toast 40 41 import java.util.Arrays 42 43 import com.example.android.downloadablefonts.Constants.ITALIC_DEFAULT 44 import com.example.android.downloadablefonts.Constants.WEIGHT_DEFAULT 45 import com.example.android.downloadablefonts.Constants.WEIGHT_MAX 46 import com.example.android.downloadablefonts.Constants.WIDTH_DEFAULT 47 import com.example.android.downloadablefonts.Constants.WIDTH_MAX 48 49 class MainActivity : AppCompatActivity() { 50 51 lateinit private var mHandler: Handler 52 53 lateinit private var mDownloadableFontTextView: TextView 54 lateinit private var mWidthSeekBar: SeekBar 55 lateinit private var mWeightSeekBar: SeekBar 56 lateinit private var mItalicSeekBar: SeekBar 57 lateinit private var mBestEffort: CheckBox 58 lateinit private var mRequestDownloadButton: Button 59 60 lateinit private var mFamilyNameSet: ArraySet<String> 61 62 override fun onCreate(savedInstanceState: Bundle?) { 63 super.onCreate(savedInstanceState) 64 setContentView(R.layout.activity_main) 65 66 val handlerThread = HandlerThread("fonts") 67 handlerThread.start() 68 mHandler = Handler(handlerThread.looper) 69 initializeSeekBars() 70 mFamilyNameSet = ArraySet<String>() 71 mFamilyNameSet.addAll(Arrays.asList(*resources.getStringArray(R.array.family_names))) 72 73 mDownloadableFontTextView = findViewById<TextView>(R.id.textview) 74 val adapter = ArrayAdapter(this, 75 android.R.layout.simple_dropdown_item_1line, 76 resources.getStringArray(R.array.family_names)) 77 val familyNameInput = findViewById<TextInputLayout>(R.id.auto_complete_family_name_input) 78 val autoCompleteFamilyName = findViewById<AutoCompleteTextView>(R.id.auto_complete_family_name) 79 autoCompleteFamilyName.setAdapter<ArrayAdapter<String>>(adapter) 80 autoCompleteFamilyName.addTextChangedListener(object : TextWatcher { 81 override fun beforeTextChanged(charSequence: CharSequence, start: Int, count: Int, 82 after: Int) { 83 // No op 84 } 85 86 override fun onTextChanged(charSequence: CharSequence, start: Int, count: Int, after: Int) { 87 if (isValidFamilyName(charSequence.toString())) { 88 familyNameInput.isErrorEnabled = false 89 familyNameInput.error = "" 90 } else { 91 familyNameInput.isErrorEnabled = true 92 familyNameInput.error = getString(R.string.invalid_family_name) 93 } 94 } 95 96 override fun afterTextChanged(editable: Editable) { 97 // No op 98 } 99 }) 100 101 mRequestDownloadButton = findViewById<Button>(R.id.button_request) 102 mRequestDownloadButton.setOnClickListener(View.OnClickListener { 103 val familyName = autoCompleteFamilyName.getText().toString() 104 if (!isValidFamilyName(familyName)) { 105 familyNameInput.isErrorEnabled = true 106 familyNameInput.error = getString(R.string.invalid_family_name) 107 Toast.makeText( 108 this@MainActivity, 109 R.string.invalid_input, 110 Toast.LENGTH_SHORT).show() 111 return@OnClickListener 112 } 113 requestDownload(familyName) 114 mRequestDownloadButton.isEnabled = false 115 }) 116 mBestEffort = findViewById<CheckBox>(R.id.checkbox_best_effort) 117 } 118 119 private fun requestDownload(familyName: String) { 120 val queryBuilder = QueryBuilder(familyName, 121 width = progressToWidth(mWidthSeekBar.progress), 122 weight = progressToWeight(mWeightSeekBar.progress), 123 italic = progressToItalic(mItalicSeekBar.progress), 124 besteffort = mBestEffort.isChecked) 125 val query = queryBuilder.build() 126 127 Log.d(TAG, "Requesting a font. Query: " + query) 128 val request = FontRequest( 129 "com.google.android.gms.fonts", 130 "com.google.android.gms", 131 query, 132 R.array.com_google_android_gms_fonts_certs) 133 134 val progressBar = findViewById<ProgressBar>(R.id.progressBar) 135 progressBar.visibility = View.VISIBLE 136 137 val callback = object : FontsContractCompat.FontRequestCallback() { 138 override fun onTypefaceRetrieved(typeface: Typeface) { 139 mDownloadableFontTextView.typeface = typeface 140 progressBar.visibility = View.GONE 141 mRequestDownloadButton.isEnabled = true 142 } 143 144 override fun onTypefaceRequestFailed(reason: Int) { 145 Toast.makeText(this@MainActivity, 146 getString(R.string.request_failed, reason), Toast.LENGTH_LONG) 147 .show() 148 progressBar.visibility = View.GONE 149 mRequestDownloadButton.isEnabled = true 150 } 151 } 152 FontsContractCompat 153 .requestFont(this@MainActivity, request, callback, mHandler) 154 } 155 156 private fun initializeSeekBars() { 157 mWidthSeekBar = findViewById<SeekBar>(R.id.seek_bar_width) 158 val widthValue = (100 * WIDTH_DEFAULT.toFloat() / WIDTH_MAX.toFloat()).toInt() 159 mWidthSeekBar.progress = widthValue 160 val widthTextView = findViewById<TextView>(R.id.textview_width) 161 widthTextView.text = widthValue.toString() 162 mWidthSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 163 override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { 164 widthTextView.text = progressToWidth(progress).toString() 165 } 166 167 override fun onStartTrackingTouch(seekBar: SeekBar) {} 168 169 override fun onStopTrackingTouch(seekBar: SeekBar) {} 170 }) 171 172 mWeightSeekBar = findViewById(R.id.seek_bar_weight) 173 val weightValue = WEIGHT_DEFAULT.toFloat() / WEIGHT_MAX.toFloat() * 100 174 mWeightSeekBar.progress = weightValue.toInt() 175 val weightTextView = findViewById<TextView>(R.id.textview_weight) 176 weightTextView.text = WEIGHT_DEFAULT.toString() 177 mWeightSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 178 override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { 179 weightTextView 180 .setText(progressToWeight(progress).toString()) 181 } 182 183 override fun onStartTrackingTouch(seekBar: SeekBar) {} 184 185 override fun onStopTrackingTouch(seekBar: SeekBar) {} 186 }) 187 188 mItalicSeekBar = findViewById<SeekBar>(R.id.seek_bar_italic) 189 mItalicSeekBar.progress = ITALIC_DEFAULT.toInt() 190 val italicTextView = findViewById<TextView>(R.id.textview_italic) 191 italicTextView.text = ITALIC_DEFAULT.toString() 192 mItalicSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 193 override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { 194 italicTextView 195 .setText(progressToItalic(progress).toString()) 196 } 197 198 override fun onStartTrackingTouch(seekBar: SeekBar) {} 199 200 override fun onStopTrackingTouch(seekBar: SeekBar) {} 201 }) 202 } 203 204 private fun isValidFamilyName(familyName: String?): Boolean { 205 return familyName != null && mFamilyNameSet.contains(familyName) 206 } 207 208 /** 209 * Converts progress from a SeekBar to the value of width. 210 * @param progress is passed from 0 to 100 inclusive 211 * * 212 * @return the converted width 213 */ 214 private fun progressToWidth(progress: Int): Float { 215 return (if (progress == 0) 1 else progress * WIDTH_MAX / 100).toFloat() 216 } 217 218 /** 219 * Converts progress from a SeekBar to the value of weight. 220 * @param progress is passed from 0 to 100 inclusive 221 * * 222 * @return the converted weight 223 */ 224 private fun progressToWeight(progress: Int): Int { 225 if (progress == 0) { 226 return 1 // The range of the weight is between (0, 1000) (exclusive) 227 } else if (progress == 100) { 228 return WEIGHT_MAX - 1 // The range of the weight is between (0, 1000) (exclusive) 229 } else { 230 return WEIGHT_MAX * progress / 100 231 } 232 } 233 234 /** 235 * Converts progress from a SeekBar to the value of italic. 236 * @param progress is passed from 0 to 100 inclusive. 237 * * 238 * @return the converted italic 239 */ 240 private fun progressToItalic(progress: Int): Float { 241 return progress.toFloat() / 100f 242 } 243 244 companion object { 245 246 private val TAG = "MainActivity" 247 } 248 } 249 250