diff --git a/build.gradle b/build.gradle index a2b3ac0dc..df022d09d 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { classpath 'com.github.ben-manes:gradle-versions-plugin:0.9' classpath 'com.android.tools.build:gradle:1.2.3' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' - classpath('fr.avianey.androidsvgdrawable:gradle-plugin:1.0.1') { + classpath('fr.avianey.androidsvgdrawable:gradle-plugin:1.0.2') { // should be excluded to avoid conflict exclude group: 'xerces' } diff --git a/twidere.component.common/build.gradle b/twidere.component.common/build.gradle index f4c8af237..8bd92dd06 100644 --- a/twidere.component.common/build.gradle +++ b/twidere.component.common/build.gradle @@ -43,7 +43,7 @@ dependencies { compile 'com.android.support:support-v4:22.2.0' compile 'com.bluelinelabs:logansquare:1.1.0' compile 'org.apache.commons:commons-lang3:3.4' - compile 'com.github.mariotaku:RestFu:b40c366f1c' + compile 'com.github.mariotaku:RestFu:d965fcf941' compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1' compile project(':twidere.component.querybuilder') compile fileTree(dir: 'libs', include: ['*.jar']) diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java index 683c75b58..66733afa7 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java @@ -181,6 +181,7 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst int TABLE_ID_CACHED_STATUSES = 62; int TABLE_ID_CACHED_HASHTAGS = 63; int TABLE_ID_CACHED_RELATIONSHIPS = 64; + int TABLE_ID_NETWORK_USAGES = 71; int VIRTUAL_TABLE_ID_DATABASE_READY = 100; int VIRTUAL_TABLE_ID_NOTIFICATIONS = 101; int VIRTUAL_TABLE_ID_PREFERENCES = 102; diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/RequestType.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/RequestType.java new file mode 100644 index 000000000..a9b79630c --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/RequestType.java @@ -0,0 +1,37 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.model; + +/** + * Created by mariotaku on 15/6/24. + */ +public enum RequestType { + API("api"), MEDIA("media"), USAGE_STATISTICS("usage_statistics"); + + public String getName() { + return name; + } + + private final String name; + + RequestType(String name) { + this.name = name; + } +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java index c5d6f7216..15240db4c 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/provider/TwidereDataStore.java @@ -35,6 +35,7 @@ public interface TwidereDataStore { String TYPE_BOOLEAN_DEFAULT_TRUE = "INTEGER(1) DEFAULT 1"; String TYPE_BOOLEAN_DEFAULT_FALSE = "INTEGER(1) DEFAULT 0"; String TYPE_TEXT = "TEXT"; + String TYPE_DOUBLE_NOT_NULL = "DOUBLE NOT NULL"; String TYPE_TEXT_NOT_NULL = "TEXT NOT NULL"; String TYPE_TEXT_NOT_NULL_UNIQUE = "TEXT NOT NULL UNIQUE"; @@ -914,4 +915,28 @@ public interface TwidereDataStore { Uri CONTENT_URI = Uri.withAppendedPath(UnreadCounts.CONTENT_URI, CONTENT_PATH_SEGMENT); } } + + interface NetworkUsages extends BaseColumns { + + String TABLE_NAME = "network_usages"; + String CONTENT_PATH = TABLE_NAME; + + Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH); + + String TIME_IN_HOURS = "time_in_hours"; + + String REQUEST_TYPE = "request_type"; + + String REQUEST_NETWORK = "request_network"; + + String KILOBYTES_SENT = "kilobytes_sent"; + + String KILOBYTES_RECEIVED = "kilobytes_received"; + + String[] COLUMNS = {_ID, TIME_IN_HOURS, REQUEST_TYPE, REQUEST_NETWORK, KILOBYTES_SENT, + KILOBYTES_RECEIVED}; + + String[] TYPES = {TYPE_PRIMARY_KEY, TYPE_INT, TYPE_TEXT_NOT_NULL, TYPE_TEXT_NOT_NULL, + TYPE_DOUBLE_NOT_NULL, TYPE_DOUBLE_NOT_NULL}; + } } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/util/MediaPreviewUtils.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/util/MediaPreviewUtils.java index ad5ca9d7f..63d89882f 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/util/MediaPreviewUtils.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/util/MediaPreviewUtils.java @@ -31,6 +31,7 @@ import org.mariotaku.restfu.http.RestHttpClient; import org.mariotaku.restfu.http.RestHttpRequest; import org.mariotaku.restfu.http.RestHttpResponse; import org.mariotaku.twidere.model.ParcelableMedia; +import org.mariotaku.twidere.model.RequestType; import org.mariotaku.twidere.util.HtmlLinkExtractor.HtmlLink; import java.io.IOException; @@ -352,6 +353,7 @@ public class MediaPreviewUtils { final RestHttpRequest.Builder builder = new RestHttpRequest.Builder(); builder.method(GET.METHOD); builder.url(Endpoint.constructUrl(URL_PHOTOZOU_PHOTO_INFO, Pair.create("photo_id", id))); + builder.extra(RequestType.MEDIA); final RestHttpResponse response = client.execute(builder.build()); final PhotoZouPhotoInfo info = LoganSquare.parse(response.getBody().stream(), PhotoZouPhotoInfo.class); if (info.info != null && info.info.photo != null) { diff --git a/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java index 3339c71b9..69fd360cc 100644 --- a/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java +++ b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java @@ -453,8 +453,8 @@ public class NyanDrawingHelper { } } - private static interface StarAnimFrames { - static final byte[][] FRAME1 = { + private interface StarAnimFrames { + byte[][] FRAME1 = { { 0, 0, 0, 0, 0, 0, 0 }, diff --git a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/Constraint.java b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/Constraint.java index 3c8d95c2c..559b4956e 100644 --- a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/Constraint.java +++ b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/Constraint.java @@ -23,8 +23,58 @@ package org.mariotaku.querybuilder; * Created by mariotaku on 15/3/30. */ public class Constraint implements SQLLang { + private final String name; + private final String type; + private final SQLQuery constraint; + + public Constraint(String name, String type, SQLQuery constraint) { + this.name = name; + this.type = type; + this.constraint = constraint; + } + @Override public String getSQL() { - return null; + final StringBuilder sb = new StringBuilder(); + if (name != null) { + sb.append("CONSTRAINT "); + sb.append(name); + sb.append(" "); + } + sb.append(type); + sb.append(" "); + sb.append(constraint.getSQL()); + return sb.toString(); } + + public static Constraint unique(String name, Columns columns, OnConflict onConflict) { + return new Constraint(name, "UNIQUE", new ColumnConflictConstaint(columns, onConflict)); + } + + public static Constraint unique(Columns columns, OnConflict onConflict) { + return unique(null, columns, onConflict); + } + + private static final class ColumnConflictConstaint implements SQLQuery { + + private final Columns columns; + private final OnConflict onConflict; + + public ColumnConflictConstaint(Columns columns, OnConflict onConflict) { + this.columns = columns; + this.onConflict = onConflict; + } + + @Override + public String getSQL() { + final StringBuilder sb = new StringBuilder(); + sb.append("("); + sb.append(columns.getSQL()); + sb.append(") "); + sb.append("ON CONFLICT "); + sb.append(onConflict.getAction()); + return sb.toString(); + } + } + } diff --git a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/RawSQLLang.java b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/RawSQLLang.java new file mode 100644 index 000000000..fd35351a3 --- /dev/null +++ b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/RawSQLLang.java @@ -0,0 +1,37 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.querybuilder; + +/** + * Created by mariotaku on 15/6/24. + */ +public final class RawSQLLang implements SQLLang { + + private final String statement; + + public RawSQLLang(String statement) { + this.statement = statement; + } + + @Override + public String getSQL() { + return statement; + } +} diff --git a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLLang.java b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLLang.java index de444fae6..41c50b8a8 100644 --- a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLLang.java +++ b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLLang.java @@ -34,5 +34,5 @@ public interface SQLLang extends Cloneable { * * @return SQL query */ - public String getSQL(); + String getSQL(); } diff --git a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java index dad9a1e48..c96751dc5 100644 --- a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java +++ b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SQLQueryBuilder.java @@ -1,11 +1,11 @@ /** * This is free and unencumbered software released into the public domain. - * + *

* Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. - * + *

* In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit @@ -13,7 +13,7 @@ * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. - + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. @@ -21,7 +21,7 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * + *

* For more information, please refer to */ @@ -110,6 +110,10 @@ public class SQLQueryBuilder { return new SQLUpdateQuery.Builder().update(onConflict, table); } + public static SQLUpdateQuery.Builder update(final OnConflict onConflict, final String table) { + return update(onConflict, new Table(table)); + } + public static SQLInsertQuery.Builder insertInto(final String table) { return insertInto(null, table); } diff --git a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SetValue.java b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SetValue.java index 8de558f17..191c66808 100644 --- a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SetValue.java +++ b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/SetValue.java @@ -15,6 +15,10 @@ public class SetValue implements SQLLang { this.expression = expression; } + public SetValue(String column, SQLLang expression) { + this(new Columns.Column(column), expression); + } + @Override public String getSQL() { diff --git a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java index c7000bd67..814d1ac27 100644 --- a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java +++ b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLCreateTableQuery.java @@ -35,8 +35,8 @@ public class SQLCreateTableQuery implements SQLQuery { if (newColumns != null && newColumns.length > 0) { sb.append('('); sb.append(Utils.toString(newColumns, ',', true)); - if (constraints != null) { - sb.append(' '); + if (constraints != null && constraints.length > 0) { + sb.append(", "); sb.append(Utils.toString(constraints, ',', true)); sb.append(' '); } diff --git a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java index 86510acee..421d9df46 100644 --- a/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java +++ b/twidere.component.querybuilder/src/main/java/org/mariotaku/querybuilder/query/SQLInsertQuery.java @@ -9,7 +9,7 @@ public class SQLInsertQuery implements SQLQuery { private OnConflict onConflict; private String table; private String[] columns; - private SQLSelectQuery select; + private String values; SQLInsertQuery() { @@ -21,11 +21,18 @@ public class SQLInsertQuery implements SQLQuery { final StringBuilder sb = new StringBuilder(); sb.append("INSERT "); if (onConflict != null) { - sb.append(String.format("OR %s ", onConflict.getAction())); + sb.append("OR "); + sb.append(onConflict.getAction()); + sb.append(" "); } - sb.append(String.format("INTO %s ", table)); - sb.append(String.format("(%s) ", Utils.toString(columns, ',', false))); - sb.append(String.format("%s ", select.getSQL())); + sb.append("INTO "); + sb.append(table); + sb.append(" ("); + sb.append(Utils.toString(columns, ',', false)); + sb.append(") "); + sb.append("VALUES ("); + sb.append(values); + sb.append(") "); return sb.toString(); } @@ -38,7 +45,11 @@ public class SQLInsertQuery implements SQLQuery { } void setSelect(final SQLSelectQuery select) { - this.select = select; + this.values = select.getSQL(); + } + + void setValues(final String... values) { + this.values = Utils.toString(values, ',', false); } void setTable(final String table) { @@ -68,6 +79,18 @@ public class SQLInsertQuery implements SQLQuery { return this; } + public Builder values(final String[] values) { + checkNotBuilt(); + query.setValues(values); + return this; + } + + public Builder values(final String values) { + checkNotBuilt(); + query.setValues(values); + return this; + } + public Builder insertInto(final OnConflict onConflict, final String table) { checkNotBuilt(); query.setOnConflict(onConflict); diff --git a/twidere/build.gradle b/twidere/build.gradle index dba8d212f..856703401 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -91,11 +91,11 @@ dependencies { compile 'com.soundcloud.android:android-crop:1.0.0@aar' compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1' compile 'com.github.mariotaku:PickNCrop:76563fae81' + compile 'com.diogobernardino:williamchart:1.7.0' googleCompile 'com.google.android.gms:play-services-maps:7.5.0' googleCompile 'com.google.maps.android:android-maps-utils:0.3.4' fdroidCompile 'org.osmdroid:osmdroid-android:4.3' fdroidCompile 'org.slf4j:slf4j-simple:1.7.12' - debugCompile 'im.dino:dbinspector:3.1.0@aar' debugCompile 'com.facebook.stetho:stetho:1.1.1' debugCompile 'com.facebook.stetho:stetho-okhttp:1.1.1' compile project(':twidere.component.common') @@ -104,9 +104,9 @@ dependencies { // googleCompile fileTree(dir: 'libs/google', include: ['*.jar']) } -task svgToPng(type: SvgDrawableTask) { +task svgToDrawable(type: SvgDrawableTask) { // specify where to pick SVG from - from = file('src/main/svg-png') + from = file('src/main/svg/drawable') // specify the android res folder to = file('src/main/res-svg2png') // create qualified directories if missing @@ -116,7 +116,9 @@ task svgToPng(type: SvgDrawableTask) { // let generate PNG for the following densities only targetedDensities = ['hdpi', 'mdpi', 'xhdpi', 'xxhdpi', 'xxxhdpi'] // relative path of the file specifying nine patch specs - ninePatchConfig = file('src/main/svg-png/9patch.json') + ninePatchConfig = file('src/main/svg/drawable/9patch.json') // output format of the generated resources outputFormat = 'PNG' + + outputType = 'drawable' } \ No newline at end of file diff --git a/twidere/src/debug/AndroidManifest.xml b/twidere/src/debug/AndroidManifest.xml deleted file mode 100644 index 4d74188ed..000000000 --- a/twidere/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/twidere/src/debug/java/org/mariotaku/twidere/util/DebugModeUtils.java b/twidere/src/debug/java/org/mariotaku/twidere/util/DebugModeUtils.java index 6be3a40f8..be13d684c 100644 --- a/twidere/src/debug/java/org/mariotaku/twidere/util/DebugModeUtils.java +++ b/twidere/src/debug/java/org/mariotaku/twidere/util/DebugModeUtils.java @@ -23,15 +23,19 @@ import android.app.Application; import com.facebook.stetho.Stetho; import com.facebook.stetho.okhttp.StethoInterceptor; +import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.OkHttpClient; +import java.util.List; + /** * Created by mariotaku on 15/5/27. */ public class DebugModeUtils { public static void initForHttpClient(final OkHttpClient client) { - client.networkInterceptors().add(new StethoInterceptor()); + final List interceptors = client.networkInterceptors(); + interceptors.add(new StethoInterceptor()); } public static void initForApplication(final Application application) { diff --git a/twidere/src/main/java/edu/tsinghua/spice/Task/SpiceAsyUploadTask.java b/twidere/src/main/java/edu/tsinghua/spice/Task/SpiceAsyUploadTask.java index f49a8609f..5f077cca3 100644 --- a/twidere/src/main/java/edu/tsinghua/spice/Task/SpiceAsyUploadTask.java +++ b/twidere/src/main/java/edu/tsinghua/spice/Task/SpiceAsyUploadTask.java @@ -13,6 +13,7 @@ import org.mariotaku.restfu.http.mime.FileTypedData; import org.mariotaku.restfu.http.mime.MultipartTypedBody; import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.Constants; +import org.mariotaku.twidere.model.RequestType; import org.mariotaku.twidere.util.TwitterAPIFactory; import java.io.File; @@ -66,6 +67,7 @@ public class SpiceAsyUploadTask extends AsyncTask implem final MultipartTypedBody body = new MultipartTypedBody(); body.add("file", new FileTypedData(tmp)); builder.body(body); + builder.extra(RequestType.USAGE_STATISTICS); final RestHttpResponse response = client.execute(builder.build()); if (response.isSuccessful()) { SpiceProfilingUtil.log("server has already received file " + tmp.getName()); diff --git a/twidere/src/main/java/org/mariotaku/twidere/Constants.java b/twidere/src/main/java/org/mariotaku/twidere/Constants.java index 40ce8a81b..d3f396a43 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/Constants.java +++ b/twidere/src/main/java/org/mariotaku/twidere/Constants.java @@ -33,7 +33,7 @@ import static org.mariotaku.twidere.annotation.Preference.Type.STRING; public interface Constants extends TwidereConstants { String DATABASES_NAME = "twidere.sqlite"; - int DATABASES_VERSION = 99; + int DATABASES_VERSION = 104; int MENU_GROUP_STATUS_EXTENSION = 10; int MENU_GROUP_COMPOSE_EXTENSION = 11; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java index 7a528a0fc..85b326d8d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java @@ -72,7 +72,6 @@ import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization; import org.mariotaku.twidere.api.twitter.auth.OAuthEndpoint; import org.mariotaku.twidere.api.twitter.auth.OAuthToken; import org.mariotaku.twidere.api.twitter.model.User; -import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment; import org.mariotaku.twidere.fragment.support.SupportProgressDialogFragment; import org.mariotaku.twidere.graphic.EmptyDrawable; @@ -123,7 +122,6 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList private LinearLayout mSignInSignUpContainer, mUsernamePasswordContainer; private final Handler mHandler = new Handler(); - private TwidereApplication mApplication; private SharedPreferences mPreferences; private ContentResolver mResolver; private AbstractSignInTask mTask; @@ -308,7 +306,6 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList super.onCreate(savedInstanceState); mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE); mResolver = getContentResolver(); - mApplication = TwidereApplication.getInstance(this); setContentView(R.layout.activity_sign_in); setSupportActionBar((Toolbar) findViewById(R.id.action_bar)); @@ -334,15 +331,17 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList mAPIChangeTimestamp = savedInstanceState.getLong(EXTRA_API_LAST_CHANGE); } - mUsernamePasswordContainer - .setVisibility(mAuthType == ParcelableCredentials.AUTH_TYPE_TWIP_O_MODE ? View.GONE : View.VISIBLE); - mSignInSignUpContainer.setOrientation(mAuthType == ParcelableCredentials.AUTH_TYPE_TWIP_O_MODE ? LinearLayout.VERTICAL - : LinearLayout.HORIZONTAL); + final boolean isTwipOMode = mAuthType == ParcelableCredentials.AUTH_TYPE_TWIP_O_MODE; + mUsernamePasswordContainer.setVisibility(isTwipOMode ? View.GONE : View.VISIBLE); + mSignInSignUpContainer.setOrientation(isTwipOMode ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL); mEditUsername.setText(mUsername); mEditUsername.addTextChangedListener(this); mEditPassword.setText(mPassword); mEditPassword.addTextChangedListener(this); + + mSignUpButton.setOnClickListener(this); + final Resources resources = getResources(); final ColorStateList color = ColorStateList.valueOf(resources.getColor(R.color.material_light_green)); ViewCompat.setBackgroundTintList(mSignInButton, color); diff --git a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java index de7d73f47..e3087d662 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java +++ b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java @@ -61,8 +61,14 @@ import com.squareup.otto.Bus; import org.apache.commons.lang3.ArrayUtils; import org.mariotaku.querybuilder.Columns.Column; import org.mariotaku.querybuilder.Expression; +import org.mariotaku.querybuilder.OnConflict; import org.mariotaku.querybuilder.RawItemArray; +import org.mariotaku.querybuilder.RawSQLLang; +import org.mariotaku.querybuilder.SQLQueryBuilder; +import org.mariotaku.querybuilder.SetValue; +import org.mariotaku.querybuilder.query.SQLInsertQuery; import org.mariotaku.querybuilder.query.SQLSelectQuery; +import org.mariotaku.querybuilder.query.SQLUpdateQuery; import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; @@ -78,6 +84,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers; import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages; import org.mariotaku.twidere.provider.TwidereDataStore.Drafts; import org.mariotaku.twidere.provider.TwidereDataStore.Mentions; +import org.mariotaku.twidere.provider.TwidereDataStore.NetworkUsages; import org.mariotaku.twidere.provider.TwidereDataStore.Preferences; import org.mariotaku.twidere.provider.TwidereDataStore.SearchHistory; import org.mariotaku.twidere.provider.TwidereDataStore.Statuses; @@ -160,6 +167,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta case TABLE_ID_DIRECT_MESSAGES_CONVERSATION: case TABLE_ID_DIRECT_MESSAGES: case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES: + case TABLE_ID_NETWORK_USAGES: return 0; } int result = 0; @@ -300,6 +308,35 @@ public final class TwidereDataProvider extends ContentProvider implements Consta rowId = mDatabaseWrapper.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE); } + } else if (tableId == TABLE_ID_NETWORK_USAGES) { + rowId = 0; + final long timeInHours = values.getAsLong(NetworkUsages.TIME_IN_HOURS); + final String requestNetwork = values.getAsString(NetworkUsages.REQUEST_NETWORK); + final String requestType = values.getAsString(NetworkUsages.REQUEST_TYPE); + final SQLInsertQuery insertOrIgnore = SQLQueryBuilder.insertInto(OnConflict.IGNORE, table) + .columns(new String[]{NetworkUsages.TIME_IN_HOURS, NetworkUsages.REQUEST_NETWORK, NetworkUsages.REQUEST_TYPE, + NetworkUsages.KILOBYTES_RECEIVED, NetworkUsages.KILOBYTES_SENT}) + .values("?, ?, ?, ?, ?") + .build(); + final SQLUpdateQuery updateIncremental = SQLQueryBuilder.update(OnConflict.REPLACE, table) + .set( + new SetValue(NetworkUsages.KILOBYTES_RECEIVED, new RawSQLLang(NetworkUsages.KILOBYTES_RECEIVED + " + ?")), + new SetValue(NetworkUsages.KILOBYTES_SENT, new RawSQLLang(NetworkUsages.KILOBYTES_SENT + " + ?")) + ) + .where(Expression.and( + Expression.equals(NetworkUsages.TIME_IN_HOURS, timeInHours), + Expression.equalsArgs(NetworkUsages.REQUEST_NETWORK), + Expression.equalsArgs(NetworkUsages.REQUEST_TYPE) + )) + .build(); + mDatabaseWrapper.beginTransaction(); + mDatabaseWrapper.execSQL(insertOrIgnore.getSQL(), + new Object[]{timeInHours, requestNetwork, requestType, 0.0, 0.0}); + mDatabaseWrapper.execSQL(updateIncremental.getSQL(), + new Object[]{values.getAsDouble(NetworkUsages.KILOBYTES_RECEIVED), + values.getAsDouble(NetworkUsages.KILOBYTES_SENT), requestNetwork, requestType}); + mDatabaseWrapper.setTransactionSuccessful(); + mDatabaseWrapper.endTransaction(); } else if (shouldReplaceOnConflict(tableId)) { rowId = mDatabaseWrapper.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE); @@ -508,6 +545,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta case TABLE_ID_DIRECT_MESSAGES_CONVERSATION: case TABLE_ID_DIRECT_MESSAGES: case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES: + case TABLE_ID_NETWORK_USAGES: return 0; } result = mDatabaseWrapper.update(table, values, selection, selectionArgs); @@ -578,6 +616,11 @@ public final class TwidereDataProvider extends ContentProvider implements Consta throw new SecurityException("Access database " + table + " requires level PERMISSION_LEVEL_READ"); break; } + default: { + if (!mPermissionsManager.checkSignature(Binder.getCallingUid())) { + throw new SecurityException("Internal database is not allowed for third-party applications"); + } + } } } @@ -618,6 +661,11 @@ public final class TwidereDataProvider extends ContentProvider implements Consta throw new SecurityException("Access database " + table + " requires level PERMISSION_LEVEL_WRITE"); break; } + default: { + if (!mPermissionsManager.checkSignature(Binder.getCallingUid())) { + throw new SecurityException("Internal database is not allowed for third-party applications"); + } + } } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/receiver/ConnectivityStateReceiver.java b/twidere/src/main/java/org/mariotaku/twidere/receiver/ConnectivityStateReceiver.java index 2c8b68a36..0a4267615 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/receiver/ConnectivityStateReceiver.java +++ b/twidere/src/main/java/org/mariotaku/twidere/receiver/ConnectivityStateReceiver.java @@ -31,6 +31,7 @@ import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.util.AsyncTaskUtils; import org.mariotaku.twidere.util.Utils; +import org.mariotaku.twidere.util.net.NetworkUsageUtils; import edu.tsinghua.spice.Task.SpiceAsyUploadTask; import edu.tsinghua.spice.Utilies.NetworkStateUtil; @@ -60,7 +61,9 @@ public class ConnectivityStateReceiver extends BroadcastReceiver implements Cons + location.getLatitude() + "," + location.getLongitude() + "," + location.getProvider()); } } - final boolean isWifi = Utils.isOnWifi(context.getApplicationContext()); + final int networkType = Utils.getActiveNetworkType(context.getApplicationContext()); + NetworkUsageUtils.setNetworkType(networkType); + final boolean isWifi = networkType == ConnectivityManager.TYPE_WIFI; final boolean isCharging = SpiceProfilingUtil.isCharging(context.getApplicationContext()); if (isWifi && isCharging) { final long currentTime = System.currentTimeMillis(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/OAuthPasswordAuthenticator.java b/twidere/src/main/java/org/mariotaku/twidere/util/OAuthPasswordAuthenticator.java index a8536ada6..ec75f8169 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/OAuthPasswordAuthenticator.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/OAuthPasswordAuthenticator.java @@ -40,6 +40,7 @@ import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.api.twitter.TwitterException; import org.mariotaku.twidere.api.twitter.TwitterOAuth; import org.mariotaku.twidere.api.twitter.auth.OAuthToken; +import org.mariotaku.twidere.model.RequestType; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; @@ -75,7 +76,7 @@ public class OAuthPasswordAuthenticator implements Constants { try { requestToken = oauth.getRequestToken(OAUTH_CALLBACK_OOB); } catch (final TwitterException e) { -// if (e.isCausedByNetworkIssue()) throw new AuthenticationException(e); + if (e.isCausedByNetworkIssue()) throw new AuthenticationException(e); throw new AuthenticityTokenException(e); } RestHttpResponse authorizePage = null, authorizeResult = null; @@ -86,6 +87,7 @@ public class OAuthPasswordAuthenticator implements Constants { authorizePageBuilder.method(GET.METHOD); authorizePageBuilder.url(endpoint.construct("/oauth/authorize", Pair.create("oauth_token", requestToken.getOauthToken()))); + authorizePageBuilder.extra(RequestType.API); final RestHttpRequest authorizePageRequest = authorizePageBuilder.build(); authorizePage = client.execute(authorizePageRequest); final String[] cookieHeaders = authorizePage.getHeaders("Set-Cookie"); @@ -120,6 +122,7 @@ public class OAuthPasswordAuthenticator implements Constants { authorizeResultBuilder.url(endpoint.construct("/oauth/authorize")); authorizeResultBuilder.headers(requestHeaders); authorizeResultBuilder.body(authorizationResultBody); + authorizeResultBuilder.extra(RequestType.API); authorizeResult = client.execute(authorizeResultBuilder.build()); final String oauthPin = readOAuthPINFromHtml(BaseTypedData.reader(authorizeResult.getBody())); if (isEmpty(oauthPin)) throw new WrongUserPassException(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/RestFuNetworkStreamDownloader.java b/twidere/src/main/java/org/mariotaku/twidere/util/RestFuNetworkStreamDownloader.java index 921ed226d..f1b729a8a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/RestFuNetworkStreamDownloader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/RestFuNetworkStreamDownloader.java @@ -29,6 +29,7 @@ import org.mariotaku.restfu.http.RestHttpRequest; import org.mariotaku.restfu.http.RestHttpResponse; import org.mariotaku.restfu.http.mime.TypedData; import org.mariotaku.twidere.activity.support.ThemedImagePickerActivity; +import org.mariotaku.twidere.model.RequestType; import java.io.IOException; @@ -46,6 +47,7 @@ public class RestFuNetworkStreamDownloader extends ThemedImagePickerActivity.Net final RestHttpRequest.Builder builder = new RestHttpRequest.Builder(); builder.method(GET.METHOD); builder.url(uri.toString()); + builder.extra(RequestType.MEDIA); final RestHttpResponse response = client.execute(builder.build()); if (response.isSuccessful()) { final TypedData body = response.getBody(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/SQLiteDatabaseWrapper.java b/twidere/src/main/java/org/mariotaku/twidere/util/SQLiteDatabaseWrapper.java index 72e58eaf6..92aa572e2 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/SQLiteDatabaseWrapper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/SQLiteDatabaseWrapper.java @@ -2,6 +2,7 @@ package org.mariotaku.twidere.util; import android.content.ContentValues; import android.database.Cursor; +import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; public class SQLiteDatabaseWrapper { @@ -47,6 +48,18 @@ public class SQLiteDatabaseWrapper { return mDatabase.insertWithOnConflict(table, nullColumnHack, initialValues, conflictAlgorithm); } + public void execSQL(String sql) throws SQLException { + tryCreateDatabase(); + if (mDatabase == null) return; + mDatabase.execSQL(sql); + } + + public void execSQL(String sql, Object[] bindArgs) throws SQLException { + tryCreateDatabase(); + if (mDatabase == null) return; + mDatabase.execSQL(sql, bindArgs); + } + public boolean isReady() { if (mLazyLoadCallback != null) return true; return mDatabase != null; diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java index e075cec22..919282561 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java @@ -44,6 +44,7 @@ import org.mariotaku.twidere.api.twitter.util.TwitterConverter; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.model.ConsumerKeyType; import org.mariotaku.twidere.model.ParcelableCredentials; +import org.mariotaku.twidere.model.RequestType; import org.mariotaku.twidere.util.net.OkHttpRestClient; import java.net.InetSocketAddress; @@ -119,7 +120,7 @@ public class TwitterAPIFactory implements TwidereConstants { client.setProxy(getProxy(prefs)); } Internal.instance.setNetwork(client, TwidereApplication.getInstance(context).getNetwork()); - return new OkHttpRestClient(client); + return new OkHttpRestClient(context, client); } @@ -373,7 +374,7 @@ public class TwitterAPIFactory implements TwidereConstants { headers.add(Pair.create("Authorization", authorization.getHeader(endpoint, info))); } headers.add(Pair.create("User-Agent", userAgent)); - return new RestHttpRequest(restMethod, url, headers, info.getBody(), null); + return new RestHttpRequest(restMethod, url, headers, info.getBody(), RequestType.API); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index b2b98a78e..3089fa24b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -214,6 +214,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Drafts; import org.mariotaku.twidere.provider.TwidereDataStore.Filters; import org.mariotaku.twidere.provider.TwidereDataStore.Filters.Users; import org.mariotaku.twidere.provider.TwidereDataStore.Mentions; +import org.mariotaku.twidere.provider.TwidereDataStore.NetworkUsages; import org.mariotaku.twidere.provider.TwidereDataStore.Notifications; import org.mariotaku.twidere.provider.TwidereDataStore.Permissions; import org.mariotaku.twidere.provider.TwidereDataStore.Preferences; @@ -319,6 +320,8 @@ public final class Utils implements Constants { TABLE_ID_SAVED_SEARCHES); CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, SearchHistory.CONTENT_PATH, TABLE_ID_SEARCH_HISTORY); + CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, NetworkUsages.CONTENT_PATH, + TABLE_ID_NETWORK_USAGES); CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Notifications.CONTENT_PATH, VIRTUAL_TABLE_ID_NOTIFICATIONS); @@ -2184,6 +2187,8 @@ public final class Utils implements Constants { return SavedSearches.TABLE_NAME; case TABLE_ID_SEARCH_HISTORY: return SearchHistory.TABLE_NAME; + case TABLE_ID_NETWORK_USAGES: + return NetworkUsages.TABLE_NAME; default: return null; } @@ -2518,6 +2523,13 @@ public final class Utils implements Constants { && networkInfo.isConnected(); } + public static int getActiveNetworkType(final Context context) { + if (context == null) return -1; + final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo networkInfo = conn.getActiveNetworkInfo(); + return networkInfo != null && networkInfo.isConnected() ? networkInfo.getType() : -1; + } + public static boolean isRedirected(final int code) { return code == 301 || code == 302 || code == 307; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java b/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java index da4f46cb2..778ca2aa8 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/content/DatabaseUpgradeHelper.java @@ -26,6 +26,7 @@ import android.text.TextUtils; import org.apache.commons.lang3.ArrayUtils; import org.mariotaku.querybuilder.Columns; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Constraint; import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.NewColumn; import org.mariotaku.querybuilder.OnConflict; @@ -48,38 +49,23 @@ import static org.mariotaku.querybuilder.SQLQueryBuilder.insertInto; import static org.mariotaku.querybuilder.SQLQueryBuilder.select; public final class DatabaseUpgradeHelper { - public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, - final String[] newColTypes, final boolean dropDirectly, final boolean strictMode, - final Map colAliases) { - safeUpgrade(db, table, newColNames, newColTypes, dropDirectly, strictMode, colAliases, OnConflict.REPLACE); - } public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, - final String[] newColTypes, final boolean dropDirectly, final boolean strictMode, - final Map colAliases, final OnConflict onConflict) { - + final String[] newColTypes, final boolean dropDirectly, + final Map colAliases, final OnConflict onConflict, + final Constraint... constraints) { if (newColNames == null || newColTypes == null || newColNames.length != newColTypes.length) throw new IllegalArgumentException("Invalid parameters for upgrading table " + table + ", length of columns and types not match."); // First, create the table if not exists. final NewColumn[] newCols = NewColumn.createNewColumns(newColNames, newColTypes); - final String createQuery = createTable(true, table).columns(newCols).buildSQL(); + final String createQuery = createTable(true, table).columns(newCols).constraint(constraints).buildSQL(); db.execSQL(createQuery); // We need to get all data from old table. final String[] oldCols = getColumnNames(db, table); - if (strictMode) { - final String oldCreate = getCreateSQL(db, table); - final Map map = getTypeMapByCreateQuery(oldCreate); - boolean different = false; - for (final NewColumn newCol : newCols) { - if (!newCol.getType().equalsIgnoreCase(map.get(newCol.getName()))) { - different = true; - } - } - if (!different) return; - } else if (oldCols == null || TwidereArrayUtils.contentMatch(newColNames, oldCols)) return; + if (oldCols == null || TwidereArrayUtils.contentMatch(newColNames, oldCols)) return; if (dropDirectly) { db.beginTransaction(); db.execSQL(dropTable(true, table).getSQL()); @@ -104,8 +90,8 @@ public final class DatabaseUpgradeHelper { } public static void safeUpgrade(final SQLiteDatabase db, final String table, final String[] newColNames, - final String[] newColTypes, final boolean dropDirectly, final Map colAliases) { - safeUpgrade(db, table, newColNames, newColTypes, dropDirectly, true, colAliases, OnConflict.REPLACE); + final String[] newColTypes, final boolean dropDirectly, final Map colAliases, final Constraint... constraints) { + safeUpgrade(db, table, newColNames, newColTypes, dropDirectly, colAliases, OnConflict.REPLACE, constraints); } private static String createInsertDataQuery(final String table, final String tempTable, final String[] newCols, diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/content/TwidereSQLiteOpenHelper.java b/twidere/src/main/java/org/mariotaku/twidere/util/content/TwidereSQLiteOpenHelper.java index 9b1f4f8c2..2aed7abeb 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/content/TwidereSQLiteOpenHelper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/content/TwidereSQLiteOpenHelper.java @@ -28,6 +28,7 @@ import android.os.Build; import org.mariotaku.querybuilder.Columns; import org.mariotaku.querybuilder.Columns.Column; +import org.mariotaku.querybuilder.Constraint; import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.NewColumn; import org.mariotaku.querybuilder.OnConflict; @@ -51,6 +52,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages; import org.mariotaku.twidere.provider.TwidereDataStore.Drafts; import org.mariotaku.twidere.provider.TwidereDataStore.Filters; import org.mariotaku.twidere.provider.TwidereDataStore.Mentions; +import org.mariotaku.twidere.provider.TwidereDataStore.NetworkUsages; import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches; import org.mariotaku.twidere.provider.TwidereDataStore.SearchHistory; import org.mariotaku.twidere.provider.TwidereDataStore.Statuses; @@ -96,6 +98,7 @@ public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements C db.execSQL(createTable(Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, true)); db.execSQL(createTable(SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true)); db.execSQL(createTable(SearchHistory.TABLE_NAME, SearchHistory.COLUMNS, SearchHistory.TYPES, true)); + db.execSQL(createTable(NetworkUsages.TABLE_NAME, NetworkUsages.COLUMNS, NetworkUsages.TYPES, true, createNetworkUsagesConstraint())); createViews(db); createTriggers(db); @@ -105,6 +108,10 @@ public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements C db.endTransaction(); } + private Constraint createNetworkUsagesConstraint() { + return Constraint.unique(new Columns(NetworkUsages.TIME_IN_HOURS, NetworkUsages.REQUEST_NETWORK, NetworkUsages.REQUEST_TYPE), OnConflict.IGNORE); + } + private void createIndices(SQLiteDatabase db) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; db.execSQL(createIndex("statuses_index", Statuses.TABLE_NAME, new String[]{Statuses.ACCOUNT_ID}, true)); @@ -248,6 +255,8 @@ public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements C safeUpgrade(db, Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, false, null); safeUpgrade(db, SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true, null); safeUpgrade(db, SearchHistory.TABLE_NAME, SearchHistory.COLUMNS, SearchHistory.TYPES, true, null); + safeUpgrade(db, NetworkUsages.TABLE_NAME, NetworkUsages.COLUMNS, NetworkUsages.TYPES, true, null, + createNetworkUsagesConstraint()); db.beginTransaction(); createViews(db); createTriggers(db); @@ -257,9 +266,10 @@ public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements C } private static String createTable(final String tableName, final String[] columns, final String[] types, - final boolean createIfNotExists) { + final boolean createIfNotExists, final Constraint... constraints) { final SQLCreateTableQuery.Builder qb = SQLQueryBuilder.createTable(createIfNotExists, tableName); qb.columns(NewColumn.createNewColumns(columns, types)); + qb.constraint(constraints); return qb.buildSQL(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java b/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java index bef8e0907..615465b4d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/imageloader/TwidereImageDownloader.java @@ -48,6 +48,7 @@ import org.mariotaku.twidere.constant.SharedPreferenceConstants; import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableCredentials; import org.mariotaku.twidere.model.ParcelableMedia; +import org.mariotaku.twidere.model.RequestType; import org.mariotaku.twidere.util.MediaPreviewUtils; import org.mariotaku.twidere.util.SharedPreferencesWrapper; import org.mariotaku.twidere.util.TwidereLinkify; @@ -186,7 +187,12 @@ public class TwidereImageDownloader extends BaseImageDownloader implements Const } else { requestUri = modifiedUri.toString(); } - final RestHttpResponse resp = mClient.execute(new RestHttpRequest.Builder().method(method).url(requestUri).headers(additionalHeaders).build()); + final RestHttpRequest.Builder builder = new RestHttpRequest.Builder(); + builder.method(method); + builder.url(requestUri); + builder.headers(additionalHeaders); + builder.extra(RequestType.MEDIA); + final RestHttpResponse resp = mClient.execute(builder.build()); final TypedData body = resp.getBody(); return new ContentLengthInputStream(body.stream(), (int) body.length()); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/NetworkUsageUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/NetworkUsageUtils.java new file mode 100644 index 000000000..3b37334e0 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/NetworkUsageUtils.java @@ -0,0 +1,90 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.util.net; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; + +import com.squareup.okhttp.Interceptor; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.ResponseBody; + +import org.mariotaku.twidere.model.RequestType; +import org.mariotaku.twidere.provider.TwidereDataStore.NetworkUsages; +import org.mariotaku.twidere.util.Utils; + +import java.io.IOException; + +/** + * Created by mariotaku on 15/6/24. + */ +public class NetworkUsageUtils { + public static void initForHttpClient(Context context, OkHttpClient client) { + client.networkInterceptors().add(new NetworkUsageInterceptor(context)); + } + + private static int sNetworkType; + + public static void setNetworkType(int networkType) { + NetworkUsageUtils.sNetworkType = networkType; + } + + private static class NetworkUsageInterceptor implements Interceptor { + private final Context context; + + public NetworkUsageInterceptor(Context context) { + this.context = context; + setNetworkType(Utils.getActiveNetworkType(context)); + } + + @Override + public Response intercept(Chain chain) throws IOException { + final Request request = chain.request(); + final Object tag = request.tag(); + if (!(tag instanceof RequestType)) return chain.proceed(request); + final ContentValues values = new ContentValues(); + values.put(NetworkUsages.TIME_IN_HOURS, System.currentTimeMillis() / 1000 / 60 / 60); + values.put(NetworkUsages.KILOBYTES_SENT, getBodyLength(request.body()) / 1024.0); + values.put(NetworkUsages.REQUEST_TYPE, ((RequestType) tag).getName()); + values.put(NetworkUsages.REQUEST_NETWORK, sNetworkType); + final Response response = chain.proceed(request); + values.put(NetworkUsages.KILOBYTES_RECEIVED, getBodyLength(response.body()) / 1024.0); + final ContentResolver cr = context.getContentResolver(); + cr.insert(NetworkUsages.CONTENT_URI, values); + return response; + } + + private long getBodyLength(RequestBody body) throws IOException { + if (body == null) return 0; + final long length = body.contentLength(); + return length > 0 ? length : 0; + } + + private long getBodyLength(ResponseBody body) throws IOException { + if (body == null) return 0; + final long length = body.contentLength(); + return length > 0 ? length : 0; + } + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpRestClient.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpRestClient.java index 32876de2d..4678b4271 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpRestClient.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpRestClient.java @@ -19,6 +19,7 @@ package org.mariotaku.twidere.util.net; +import android.content.Context; import android.os.Looper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -59,12 +60,9 @@ public class OkHttpRestClient implements RestHttpClient { private final OkHttpClient client; - public OkHttpRestClient() { - this(new OkHttpClient()); - } - - public OkHttpRestClient(OkHttpClient client) { + public OkHttpRestClient(Context context, OkHttpClient client) { this.client = client; + NetworkUsageUtils.initForHttpClient(context, client); DebugModeUtils.initForHttpClient(client); } @@ -85,6 +83,7 @@ public class OkHttpRestClient implements RestHttpClient { builder.addHeader(header.first, header.second); } } + builder.tag(restHttpRequest.getExtra()); return client.newCall(builder.build()); } @@ -132,6 +131,11 @@ public class OkHttpRestClient implements RestHttpClient { body.writeTo(sink.outputStream()); } + @Override + public long contentLength() throws IOException { + return body.length(); + } + @Nullable public static RequestBody wrap(@Nullable TypedData body) { if (body == null) return null; diff --git a/twidere/src/main/res/layout/activity_sign_in.xml b/twidere/src/main/res/layout/activity_sign_in.xml index 6b296deeb..7d852f123 100644 --- a/twidere/src/main/res/layout/activity_sign_in.xml +++ b/twidere/src/main/res/layout/activity_sign_in.xml @@ -85,7 +85,6 @@ android:layout_height="wrap_content" android:layout_weight="1" android:minHeight="48dp" - android:onClick="onClick" android:text="@string/register"/>