adding google drive sync

This commit is contained in:
Mariotaku Lee 2017-01-19 19:43:20 +08:00
parent a97edf5571
commit dcd6c81bcd
17 changed files with 406 additions and 111 deletions

View File

@ -98,6 +98,7 @@ dependencies {
// START Non-FOSS component
googleCompile "com.google.android.gms:play-services-maps:$play_services_version"
googleCompile "com.google.android.gms:play-services-auth:$play_services_version"
googleCompile "com.google.android.gms:play-services-drive:$play_services_version"
googleCompile 'com.google.maps.android:android-maps-utils:0.4.4'
googleCompile('com.crashlytics.sdk.android:crashlytics:2.6.5@aar') { transitive = true }
googleCompile 'com.anjlab.android.iab.v3:library:1.0.38'

View File

@ -31,7 +31,10 @@
</intent-filter>
</activity>
<activity
android:name=".activity.DropboxAuthStarterActivity"
android:name=".activity.sync.DropboxAuthStarterActivity"
android:theme="@style/Theme.Twidere.NoDisplay"/>
<activity
android:name=".activity.sync.GoogleDriveAuthActivity"
android:theme="@style/Theme.Twidere.NoDisplay"/>
<activity
android:name="com.dropbox.core.android.AuthActivity"

View File

@ -1,9 +1,10 @@
package org.mariotaku.twidere.activity
package org.mariotaku.twidere.activity.sync
import android.os.Bundle
import com.dropbox.core.android.Auth
import org.mariotaku.kpreferences.set
import org.mariotaku.twidere.Constants.DROPBOX_APP_KEY
import org.mariotaku.twidere.activity.BaseActivity
import org.mariotaku.twidere.constant.dataSyncProviderInfoKey
import org.mariotaku.twidere.model.sync.DropboxSyncProviderInfo

View File

@ -0,0 +1,79 @@
package org.mariotaku.twidere.activity.sync
import android.app.Activity
import android.content.Intent
import android.content.IntentSender
import android.os.Bundle
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GooglePlayServicesUtil
import com.google.android.gms.common.api.GoogleApiClient
import com.google.android.gms.drive.Drive
import org.mariotaku.kpreferences.set
import org.mariotaku.twidere.activity.BaseActivity
import org.mariotaku.twidere.constant.dataSyncProviderInfoKey
import org.mariotaku.twidere.model.sync.GoogleDriveSyncProviderInfo
class GoogleDriveAuthActivity : BaseActivity(), GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private lateinit var googleApiClient: GoogleApiClient
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
googleApiClient = GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_APPFOLDER)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build()
}
override fun onStart() {
super.onStart()
googleApiClient.connect()
}
override fun onStop() {
googleApiClient.disconnect()
super.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
when (requestCode) {
RESOLVE_CONNECTION_REQUEST_CODE -> if (resultCode == Activity.RESULT_OK) {
googleApiClient.connect()
}
}
}
override fun onConnectionFailed(connectionResult: ConnectionResult) {
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(this, RESOLVE_CONNECTION_REQUEST_CODE)
} catch (e: IntentSender.SendIntentException) {
// Unable to resolve, message user appropriately
}
} else {
preferences[dataSyncProviderInfoKey] = null
GooglePlayServicesUtil.showErrorDialogFragment(connectionResult.errorCode, this, null, 0) {
finish()
}
}
}
override fun onConnected(connectionHint: Bundle?) {
preferences[dataSyncProviderInfoKey] = GoogleDriveSyncProviderInfo()
finish()
}
override fun onConnectionSuspended(cause: Int) {
finish()
}
companion object {
private const val RESOLVE_CONNECTION_REQUEST_CODE: Int = 101
}
}

View File

@ -0,0 +1,24 @@
package org.mariotaku.twidere.model.sync
import android.content.Context
import android.content.SharedPreferences
import org.mariotaku.twidere.util.sync.SyncTaskRunner
import org.mariotaku.twidere.util.sync.google.GoogleDriveSyncTaskRunner
class GoogleDriveSyncProviderInfo : SyncProviderInfo(GoogleDriveSyncProviderInfo.TYPE) {
override fun writeToPreferences(editor: SharedPreferences.Editor) {
}
override fun newSyncTaskRunner(context: Context): SyncTaskRunner {
return GoogleDriveSyncTaskRunner(context)
}
companion object {
const val TYPE = "google_drive"
fun newInstance(preferences: SharedPreferences): GoogleDriveSyncProviderInfo? {
return GoogleDriveSyncProviderInfo()
}
}
}

View File

@ -1,11 +1,14 @@
package org.mariotaku.twidere.util.sync
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.DropboxAuthStarterActivity
import org.mariotaku.twidere.activity.sync.DropboxAuthStarterActivity
import org.mariotaku.twidere.activity.sync.GoogleDriveAuthActivity
import org.mariotaku.twidere.model.sync.DropboxSyncProviderInfo
import org.mariotaku.twidere.model.sync.GoogleDriveSyncProviderInfo
import org.mariotaku.twidere.model.sync.SyncProviderEntry
import org.mariotaku.twidere.model.sync.SyncProviderInfo
@ -17,6 +20,7 @@ class NonFreeSyncProviderInfoFactory : SyncProviderInfoFactory() {
override fun getInfoForType(type: String, preferences: SharedPreferences): SyncProviderInfo? {
return when (type) {
DropboxSyncProviderInfo.TYPE -> DropboxSyncProviderInfo.newInstance(preferences)
GoogleDriveSyncProviderInfo.TYPE -> GoogleDriveSyncProviderInfo.newInstance(preferences)
else -> null
}
}
@ -25,7 +29,11 @@ class NonFreeSyncProviderInfoFactory : SyncProviderInfoFactory() {
return listOf(
SyncProviderEntry(DropboxSyncProviderInfo.TYPE,
context.getString(R.string.sync_provider_name_dropbox),
Intent(context, DropboxAuthStarterActivity::class.java))
Intent(context, DropboxAuthStarterActivity::class.java)),
SyncProviderEntry(GoogleDriveSyncProviderInfo.TYPE,
context.getString(R.string.sync_provider_name_google_drive),
Intent(context, GoogleDriveAuthActivity::class.java))
)
}
}

View File

@ -15,7 +15,10 @@ import org.mariotaku.twidere.util.sync.FileBasedDraftsSyncAction
import java.io.IOException
import java.util.*
class DropboxDraftsSyncAction(context: Context, val client: DbxClientV2) : FileBasedDraftsSyncAction<FileMetadata>(context) {
class DropboxDraftsSyncAction(
context: Context,
val client: DbxClientV2
) : FileBasedDraftsSyncAction<FileMetadata>(context) {
@Throws(IOException::class)
override fun Draft.saveToRemote(): FileMetadata {
try {

View File

@ -0,0 +1,96 @@
package org.mariotaku.twidere.util.sync.google
import android.content.Context
import com.google.android.gms.common.api.GoogleApiClient
import com.google.android.gms.drive.Drive
import com.google.android.gms.drive.DriveFile
import com.google.android.gms.drive.DriveId
import com.google.android.gms.drive.MetadataChangeSet
import org.mariotaku.twidere.extension.model.filename
import org.mariotaku.twidere.extension.model.readMimeMessageFrom
import org.mariotaku.twidere.extension.model.writeMimeMessageTo
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.util.sync.FileBasedDraftsSyncAction
import java.io.IOException
import java.util.*
class GoogleDriveDraftsSyncAction(
context: Context,
val client: GoogleApiClient
) : FileBasedDraftsSyncAction<GoogleDriveDraftsSyncAction.DriveFileInfo>(context) {
@Throws(IOException::class)
override fun Draft.saveToRemote(): DriveFileInfo {
try {
val folder = Drive.DriveApi.getAppFolder(client)
val driveContents = Drive.DriveApi.newDriveContents(client).await().driveContents
this.writeMimeMessageTo(context, driveContents.outputStream)
val filename = "/Drafts/$filename"
val changeSet = MetadataChangeSet.Builder().setTitle(filename).build()
val driveFile = folder.createFile(client, changeSet, driveContents).await().driveFile
return DriveFileInfo(driveFile.driveId, filename, Date())
} catch (e: Exception) {
throw IOException(e)
}
}
@Throws(IOException::class)
override fun Draft.loadFromRemote(info: DriveFileInfo): Boolean {
try {
val file = info.driveId.asDriveFile()
val result = file.open(client, DriveFile.MODE_READ_ONLY, null).await()
result.driveContents.inputStream.use {
val parsed = this.readMimeMessageFrom(context, it)
if (parsed) {
this.timestamp = info.draftTimestamp
this.unique_id = info.draftFileName.substringBeforeLast(".eml")
}
return parsed
}
} catch (e: Exception) {
throw IOException(e)
}
}
@Throws(IOException::class)
override fun removeDrafts(list: List<DriveFileInfo>): Boolean {
try {
list.forEach { info ->
info.driveId.asDriveFile().delete(client).await()
}
return true
} catch (e: Exception) {
throw IOException(e)
}
}
@Throws(IOException::class)
override fun removeDraft(info: DriveFileInfo): Boolean {
try {
return info.driveId.asDriveFile().delete(client).await().isSuccess
} catch (e: Exception) {
throw IOException(e)
}
}
override val DriveFileInfo.draftTimestamp: Long get() = this.modifiedDate.time
override val DriveFileInfo.draftFileName: String get() = this.name
@Throws(IOException::class)
override fun listRemoteDrafts(): List<DriveFileInfo> {
val pendingResult = Drive.DriveApi.getAppFolder(client).listChildren(client)
val result = ArrayList<DriveFileInfo>()
try {
val requestResult = pendingResult.await()
requestResult.metadataBuffer.mapTo(result) { metadata ->
DriveFileInfo(metadata.driveId, metadata.originalFilename, metadata.modifiedDate)
}
} catch (e: Exception) {
throw IOException(e)
}
return result
}
data class DriveFileInfo(val driveId: DriveId, val name: String, val modifiedDate: Date)
}

View File

@ -1,11 +1,56 @@
package org.mariotaku.twidere.util.sync.google
import android.content.ComponentCallbacks
import android.content.Context
import android.os.Bundle
import com.dropbox.core.DbxRequestConfig
import com.dropbox.core.v2.DbxClientV2
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.api.GoogleApiClient
import com.google.android.gms.drive.Drive
import nl.komponents.kovenant.task
import nl.komponents.kovenant.then
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.util.TaskServiceRunner
import org.mariotaku.twidere.util.sync.ISyncAction
import org.mariotaku.twidere.util.sync.SyncTaskRunner
import org.mariotaku.twidere.util.sync.UserColorsSyncProcessor
import org.mariotaku.twidere.util.sync.UserNicknamesSyncProcessor
import org.mariotaku.twidere.util.sync.dropbox.DropboxDraftsSyncAction
import org.mariotaku.twidere.util.sync.dropbox.DropboxFiltersDataSyncAction
import org.mariotaku.twidere.util.sync.dropbox.DropboxPreferencesValuesSyncAction
import java.net.ConnectException
/**
* Created by mariotaku on 2017/1/6.
*/
class GoogleDriveSyncTaskRunner(context: Context) {
class GoogleDriveSyncTaskRunner(context: Context) : SyncTaskRunner(context) {
override fun onRunningTask(action: String, callback: (Boolean) -> Unit): Boolean {
val client = GoogleApiClient.Builder(context)
.addApi(Drive.API)
.addScope(Drive.SCOPE_APPFOLDER)
.build()
val syncAction: ISyncAction = when (action) {
TaskServiceRunner.ACTION_SYNC_DRAFTS -> GoogleDriveDraftsSyncAction(context, client)
else -> null
} ?: return false
task {
val connResult = client.blockingConnect()
if (!connResult.isSuccess) {
throw ConnectException()
}
syncAction.execute()
}.successUi {
callback(true)
}.failUi {
callback(false)
}.always {
client.disconnect()
}
return true
}
}

View File

@ -477,6 +477,7 @@
<activity
android:name=".activity.PremiumDashboardActivity"
android:label="@string/title_premium_features_name"
android:parentActivityName=".activity.HomeActivity"
android:theme="@style/Theme.Twidere">
<meta-data

View File

@ -8,7 +8,8 @@
"same_oauth_url": true,
"no_version_suffix": false,
"consumer_key": "i5XtSVfoWjLjKlnrvhiLPMZC0",
"consumer_secret": "sQncmZ2atQR6tKbqnnAtqjrECqN8k6FD4p4OoNS0XTDkUz3HGH"
"consumer_secret": "sQncmZ2atQR6tKbqnnAtqjrECqN8k6FD4p4OoNS0XTDkUz3HGH",
"sign_up_url": "https://twitter.com/signup"
},
{
"name": "Fanfou",
@ -19,7 +20,8 @@
"same_oauth_url": true,
"no_version_suffix": true,
"consumer_key": "86d1146dda1d21d59351008a1d1058fd",
"consumer_secret": "c00f4b83dbfc52e2ed78a21d4edfc3cc"
"consumer_secret": "c00f4b83dbfc52e2ed78a21d4edfc3cc",
"sign_up_url": "http://fanfou.com/register"
},
{
"name": "Quitter.se",
@ -30,7 +32,8 @@
"same_oauth_url": true,
"no_version_suffix": true,
"consumer_key": "3584cd86bd8e04948be15650acbd0d59",
"consumer_secret": "84a39eff81c0a0ecbfba5239d25c0367"
"consumer_secret": "84a39eff81c0a0ecbfba5239d25c0367",
"sign_up_url": "https://quitter.se/"
},
{
"name": "LoadAverage.org",

View File

@ -55,6 +55,9 @@ public final class CustomAPIConfig implements Parcelable {
String consumerKey;
@JsonField(name = "consumer_secret")
String consumerSecret;
@Nullable
@JsonField(name = "sign_up_url")
String signUpUrl;
public CustomAPIConfig() {
}
@ -139,6 +142,11 @@ public final class CustomAPIConfig implements Parcelable {
this.noVersionSuffix = noVersionSuffix;
}
@Nullable
public String getSignUpUrl() {
return signUpUrl;
}
@Override
public int describeContents() {
return 0;

View File

@ -24,6 +24,7 @@ import android.accounts.AccountAuthenticatorResponse
import android.accounts.AccountManager
import android.app.Activity
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
@ -31,6 +32,7 @@ import android.content.res.ColorStateList
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.support.customtabs.CustomTabsIntent
import android.support.v4.app.DialogFragment
import android.support.v4.content.ContextCompat
import android.support.v4.util.ArraySet
@ -52,6 +54,8 @@ import android.widget.Toast
import com.bluelinelabs.logansquare.LoganSquare
import com.rengwuxian.materialedittext.MaterialEditText
import kotlinx.android.synthetic.main.activity_sign_in.*
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.*
import org.mariotaku.microblog.library.MicroBlog
@ -71,6 +75,7 @@ import org.mariotaku.twidere.activity.iface.APIEditorActivity
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_API_CONFIG
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_CREDENTIALS_TYPE
import org.mariotaku.twidere.constant.chromeCustomTabKey
import org.mariotaku.twidere.constant.defaultAPIConfigKey
import org.mariotaku.twidere.constant.randomizeAccountNameKey
import org.mariotaku.twidere.extension.model.getColor
@ -125,7 +130,6 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
val isTwipOMode = apiConfig.credentialsType == Credentials.Type.EMPTY
usernamePasswordContainer.visibility = if (isTwipOMode) View.GONE else View.VISIBLE
signInSignUpContainer.orientation = if (isTwipOMode) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL
editUsername.addTextChangedListener(this)
editPassword.addTextChangedListener(this)
@ -208,15 +212,12 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
when (apiConfig.credentialsType) {
Credentials.Type.XAUTH, Credentials.Type.BASIC -> {
usernamePasswordContainer.visibility = View.VISIBLE
signInSignUpContainer.orientation = LinearLayout.HORIZONTAL
}
Credentials.Type.EMPTY -> {
usernamePasswordContainer.visibility = View.GONE
signInSignUpContainer.orientation = LinearLayout.VERTICAL
}
else -> {
usernamePasswordContainer.visibility = View.GONE
signInSignUpContainer.orientation = LinearLayout.VERTICAL
}
}
}
@ -224,8 +225,25 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
override fun onClick(v: View) {
when (v) {
signUp -> {
val intent = Intent(Intent.ACTION_VIEW).setData(Uri.parse(TWITTER_SIGNUP_URL))
startActivity(intent)
val signUpUrl = apiConfig.signUpUrl ?: return
val uri = Uri.parse(signUpUrl)
if (preferences[chromeCustomTabKey]) {
val builder = CustomTabsIntent.Builder()
builder.setToolbarColor(overrideTheme.colorToolbar)
val intent = builder.build()
try {
intent.launchUrl(this, uri)
} catch (e: ActivityNotFoundException) {
// Ignore
}
} else {
val intent = Intent(Intent.ACTION_VIEW, uri)
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
// Ignore
}
}
}
signIn -> {
if (usernamePasswordContainer.visibility != View.VISIBLE) {
@ -235,11 +253,9 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
doLogin()
}
passwordSignIn -> {
executeAfterFragmentResumed {
val fm = supportFragmentManager
executeAfterFragmentResumed { fragment ->
val df = PasswordSignInDialogFragment()
df.show(fm.beginTransaction(), "password_sign_in")
Unit
df.show(fragment.supportFragmentManager, "password_sign_in")
}
}
}
@ -377,6 +393,16 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
signIn.isEnabled = true
}
}
signUp.visibility = if (apiConfig.signUpUrl != null) {
View.VISIBLE
} else {
View.GONE
}
passwordSignIn.visibility = if (apiConfig.type == null || apiConfig.type == AccountType.TWITTER) {
View.VISIBLE
} else {
View.GONE
}
}
internal fun onSignInResult(result: SignInResponse) {

View File

@ -37,6 +37,7 @@ import android.widget.RadioGroup
import android.widget.RadioGroup.OnCheckedChangeListener
import android.widget.TextView
import android.widget.Toast
import com.bluelinelabs.logansquare.LoganSquare
import kotlinx.android.synthetic.main.activity_api_editor.*
import kotlinx.android.synthetic.main.layout_api_editor.*
import kotlinx.android.synthetic.main.layout_api_editor_advanced_fields.*
@ -63,6 +64,38 @@ import javax.inject.Inject
class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListener, CompoundButton.OnCheckedChangeListener {
private var editNoVersionSuffixChanged: Boolean = false
private lateinit var apiConfig: CustomAPIConfig
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_api_editor)
if (savedInstanceState != null) {
apiConfig = savedInstanceState.getParcelable(EXTRA_API_CONFIG)
} else {
apiConfig = intent.getParcelableExtra(EXTRA_API_CONFIG) ?: kPreferences[defaultAPIConfigKey]
}
editAuthType.setOnCheckedChangeListener(this)
editNoVersionSuffix.setOnCheckedChangeListener(this)
save.setOnClickListener(this)
apiUrlFormatHelp.setOnClickListener(this)
loadDefaults.visibility = View.VISIBLE
loadDefaults.setOnClickListener(this)
editApiUrlFormat.setText(apiConfig.apiUrlFormat)
editSameOAuthSigningUrl.isChecked = apiConfig.isSameOAuthUrl
editNoVersionSuffix.isChecked = apiConfig.isNoVersionSuffix
editConsumerKey.setText(apiConfig.consumerKey)
editConsumerSecret.setText(apiConfig.consumerSecret)
editAuthType.check(getAuthTypeId(apiConfig.credentialsType))
if (editAuthType.checkedRadioButtonId == -1) {
oauth.isChecked = true
}
}
override fun onCheckedChanged(group: RadioGroup, checkedId: Int) {
val authType = getCheckedAuthType(checkedId)
val isOAuth = authType == Credentials.Type.OAUTH || authType == Credentials.Type.XAUTH
@ -97,10 +130,6 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
}
}
private fun checkApiUrl(): Boolean {
return MicroBlogAPIFactory.verifyApiFormat(editApiUrlFormat.text.toString())
}
public override fun onSaveInstanceState(outState: Bundle) {
outState.putParcelable(EXTRA_API_CONFIG, createCustomAPIConfig())
super.onSaveInstanceState(outState)
@ -113,8 +142,22 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
finish()
}
private fun checkApiUrl(): Boolean {
return MicroBlogAPIFactory.verifyApiFormat(editApiUrlFormat.text.toString())
}
private fun applyApiConfig() {
editApiUrlFormat.setText(apiConfig.apiUrlFormat)
editAuthType.check(getAuthTypeId(apiConfig.credentialsType))
editSameOAuthSigningUrl.isChecked = apiConfig.isSameOAuthUrl
editNoVersionSuffix.isChecked = apiConfig.isNoVersionSuffix
editConsumerKey.setText(apiConfig.consumerKey)
editConsumerSecret.setText(apiConfig.consumerSecret)
}
private fun createCustomAPIConfig(): CustomAPIConfig {
return CustomAPIConfig().apply {
return apiConfig.apply {
this.apiUrlFormat = editApiUrlFormat.text.toString()
this.credentialsType = getCheckedAuthType(editAuthType.checkedRadioButtonId)
this.isSameOAuthUrl = editSameOAuthSigningUrl.isChecked
@ -124,53 +167,12 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_api_editor)
val apiConfig: CustomAPIConfig
if (savedInstanceState != null) {
apiConfig = savedInstanceState.getParcelable(EXTRA_API_CONFIG)
} else {
apiConfig = intent.getParcelableExtra(EXTRA_API_CONFIG) ?: kPreferences[defaultAPIConfigKey]
}
editAuthType.setOnCheckedChangeListener(this)
editNoVersionSuffix.setOnCheckedChangeListener(this)
save.setOnClickListener(this)
apiUrlFormatHelp.setOnClickListener(this)
loadDefaults.visibility = View.VISIBLE
loadDefaults.setOnClickListener(this)
editApiUrlFormat.setText(apiConfig.apiUrlFormat)
editSameOAuthSigningUrl.isChecked = apiConfig.isSameOAuthUrl
editNoVersionSuffix.isChecked = apiConfig.isNoVersionSuffix
editConsumerKey.setText(apiConfig.consumerKey)
editConsumerSecret.setText(apiConfig.consumerSecret)
editAuthType.check(getAuthTypeId(apiConfig.credentialsType))
if (editAuthType.checkedRadioButtonId == -1) {
oauth.isChecked = true
}
}
private fun setAPIConfig(apiConfig: CustomAPIConfig) {
editApiUrlFormat.setText(apiConfig.apiUrlFormat)
editAuthType.check(getAuthTypeId(apiConfig.credentialsType))
editSameOAuthSigningUrl.isChecked = apiConfig.isSameOAuthUrl
editNoVersionSuffix.isChecked = apiConfig.isNoVersionSuffix
editConsumerKey.setText(apiConfig.consumerKey)
editConsumerSecret.setText(apiConfig.consumerSecret)
}
class LoadDefaultsChooserDialogFragment : BaseDialogFragment(), DialogInterface.OnClickListener,
LoaderManager.LoaderCallbacks<List<CustomAPIConfig>?> {
private lateinit var adapter: ArrayAdapter<CustomAPIConfig>
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val context = context
val configs = CustomAPIConfig.listDefault(context)
adapter = CustomAPIConfigArrayAdapter(context, configs)
val builder = AlertDialog.Builder(context)
@ -182,7 +184,9 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
}
override fun onClick(dialog: DialogInterface, which: Int) {
(activity as APIEditorActivity).setAPIConfig(adapter.getItem(which))
val activity = activity as APIEditorActivity
activity.apiConfig = adapter.getItem(which)
activity.applyApiConfig()
dismiss()
}
@ -212,17 +216,16 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
override fun loadInBackground(): List<CustomAPIConfig>? {
val request = HttpRequest(GET.METHOD, DEFAULT_API_CONFIGS_URL,
null, null, null)
var response: HttpResponse? = null
try {
response = client.newCall(request).execute()
if (response!!.isSuccessful) {
val `is` = response.body.stream()
return JsonSerializer.parseList(`is`, CustomAPIConfig::class.java)
return client.newCall(request).execute().use { response ->
if (response.isSuccessful) {
return@use LoganSquare.parseList(response.body.stream(),
CustomAPIConfig::class.java)
}
return@use null
}
} catch (e: IOException) {
// Ignore
} finally {
Utils.closeSilently(response)
}
return null
}

View File

@ -19,6 +19,7 @@ import org.mariotaku.twidere.fragment.BaseFragment
import org.mariotaku.twidere.fragment.ExtraFeaturesIntroductionDialogFragment
import org.mariotaku.twidere.fragment.sync.SyncSettingsFragment
import org.mariotaku.twidere.model.analyzer.PurchaseFinished
import org.mariotaku.twidere.model.sync.SyncProviderEntry
import org.mariotaku.twidere.util.Analyzer
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
import org.mariotaku.twidere.util.sync.SyncProviderInfoFactory
@ -77,12 +78,14 @@ class SyncStatusFragment : BaseFragment() {
}
private fun updateSyncSettingActions() {
if (preferences[dataSyncProviderInfoKey] == null) {
val providerInfo = preferences[dataSyncProviderInfoKey]
if (providerInfo == null) {
statusText.text = getText(R.string.message_sync_data_connect_hint)
connectButton.visibility = View.VISIBLE
settingsButton.visibility = View.GONE
} else {
statusText.text = getString(R.string.message_sync_data_synced_with_name, "Dropbox")
val providerEntry = SyncProviderInfoFactory.getProviderEntry(context, providerInfo.type)!!
statusText.text = getString(R.string.message_sync_data_synced_with_name, providerEntry.name)
connectButton.visibility = View.GONE
settingsButton.visibility = View.VISIBLE
}

View File

@ -17,8 +17,7 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -44,8 +43,8 @@
android:layout_height="wrap_content"
android:hint="@string/username"
android:inputType="textEmailAddress"
android:typeface="normal"
android:maxLines="1"/>
android:maxLines="1"
android:typeface="normal" />
<EditText
android:id="@+id/editPassword"
@ -54,49 +53,39 @@
android:layout_marginTop="8dp"
android:hint="@string/password"
android:inputType="textPassword"
android:typeface="normal"
android:maxLines="1"/>
android:maxLines="1"
android:typeface="normal" />
</LinearLayout>
<LinearLayout
android:id="@+id/signInSignUpContainer"
<android.support.v7.widget.AppCompatButton
android:id="@+id/signIn"
style="?android:buttonStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:orientation="horizontal"
android:padding="8dp">
android:minHeight="@dimen/element_size_normal"
android:text="@string/sign_in"
app:backgroundTint="@color/material_light_green" />
<android.support.v7.widget.AppCompatButton
android:id="@+id/signUp"
style="?android:buttonStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="@dimen/element_size_normal"
android:text="@string/register"/>
<android.support.v7.widget.AppCompatButton
android:id="@+id/signIn"
style="?android:buttonStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="@dimen/element_size_normal"
android:text="@string/sign_in"
app:backgroundTint="@color/material_light_green"/>
</LinearLayout>
<Button
android:id="@+id/signUp"
style="?android:borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:minHeight="@dimen/element_size_xsmall"
android:text="@string/register"
android:textAppearance="?android:textAppearanceSmall" />
<Button
android:id="@+id/passwordSignIn"
style="?android:borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="36dp"
android:text="@string/password_sign_in_twitter_only"
android:textAppearance="?android:textAppearanceSmall"/>
android:gravity="start|center_vertical"
android:minHeight="@dimen/element_size_xsmall"
android:text="@string/label_password_sign_in"
android:textAppearance="?android:textAppearanceSmall" />
</LinearLayout>
</ScrollView>

View File

@ -888,6 +888,7 @@
<string name="message_sync_data_synced_with_name">Twidere is now synced with <xliff:g example="ownCloud" id="name">%s</xliff:g></string>
<string name="title_sync">Data sync</string>
<string name="sync_provider_name_dropbox">Dropbox</string>
<string name="sync_provider_name_google_drive">Google Drive</string>
<!-- [verb] Disconnect from network storage -->
<string name="action_sync_disconnect">Disconnect</string>
<string name="action_sync_sync_now">Sync now</string>
@ -904,4 +905,5 @@
<string name="title_filters_subscription_url">URL</string>
<string name="label_filters_subscription">Subscription</string>
<string name="summary_interactions_not_available">Only available with official keys</string>
<string name="label_password_sign_in">Password sign in</string>
</resources>