Initial Alpha release!
1
app/.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/build
|
/build
|
||||||
|
app-release.apk
|
|
@ -8,7 +8,7 @@ android {
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0.0-alpha.1"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary true
|
vectorDrawables.useSupportLibrary true
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@drawable/ic_logo"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
|
BIN
app/src/main/ic_launcher-web.png
Normal file
After Width: | Height: | Size: 18 KiB |
|
@ -28,7 +28,6 @@ import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
|
@ -45,6 +45,8 @@ import java.util.Map;
|
||||||
|
|
||||||
public class AccountFragment extends Fragment implements AccountActionListener,
|
public class AccountFragment extends Fragment implements AccountActionListener,
|
||||||
FooterActionListener {
|
FooterActionListener {
|
||||||
|
private static final String TAG = "Account";
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
FOLLOWS,
|
FOLLOWS,
|
||||||
FOLLOWERS,
|
FOLLOWERS,
|
||||||
|
@ -166,7 +168,7 @@ public class AccountFragment extends Fragment implements AccountActionListener,
|
||||||
try {
|
try {
|
||||||
accounts = Account.parse(response);
|
accounts = Account.parse(response);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
onFetchAccountsFailure();
|
onFetchAccountsFailure(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onFetchAccountsSuccess(accounts, fromId != null);
|
onFetchAccountsSuccess(accounts, fromId != null);
|
||||||
|
@ -175,7 +177,7 @@ public class AccountFragment extends Fragment implements AccountActionListener,
|
||||||
new Response.ErrorListener() {
|
new Response.ErrorListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onErrorResponse(VolleyError error) {
|
public void onErrorResponse(VolleyError error) {
|
||||||
onFetchAccountsFailure();
|
onFetchAccountsFailure(error);
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -201,8 +203,9 @@ public class AccountFragment extends Fragment implements AccountActionListener,
|
||||||
showFetchAccountsRetry(false);
|
showFetchAccountsRetry(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFetchAccountsFailure() {
|
private void onFetchAccountsFailure(Exception exception) {
|
||||||
showFetchAccountsRetry(true);
|
showFetchAccountsRetry(true);
|
||||||
|
Log.e(TAG, "Fetch failure: " + exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showFetchAccountsRetry(boolean show) {
|
private void showFetchAccountsRetry(boolean show) {
|
||||||
|
|
|
@ -404,8 +404,9 @@ public class ComposeActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private void readyStatus(final String content, final String visibility,
|
private void readyStatus(final String content, final String visibility,
|
||||||
final boolean sensitive, final String spoilerText) {
|
final boolean sensitive, final String spoilerText) {
|
||||||
final ProgressDialog dialog = ProgressDialog.show(this, "Finishing Media Upload",
|
final ProgressDialog dialog = ProgressDialog.show(
|
||||||
"Uploading...", true, true);
|
this, getString(R.string.dialog_title_finishing_media_upload),
|
||||||
|
getString(R.string.dialog_message_uploading_media), true, true);
|
||||||
final AsyncTask<Void, Void, Boolean> waitForMediaTask =
|
final AsyncTask<Void, Void, Boolean> waitForMediaTask =
|
||||||
new AsyncTask<Void, Void, Boolean>() {
|
new AsyncTask<Void, Void, Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -734,6 +735,10 @@ public class ComposeActivity extends AppCompatActivity {
|
||||||
Uri uri = data.getData();
|
Uri uri = data.getData();
|
||||||
ContentResolver contentResolver = getContentResolver();
|
ContentResolver contentResolver = getContentResolver();
|
||||||
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
|
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
|
||||||
|
if (cursor == null) {
|
||||||
|
displayTransientError(R.string.error_media_upload_opening);
|
||||||
|
return;
|
||||||
|
}
|
||||||
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
|
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
long mediaSize = cursor.getLong(sizeIndex);
|
long mediaSize = cursor.getLong(sizeIndex);
|
||||||
|
|
51
app/src/main/java/com/keylesspalace/tusky/Log.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/* Copyright 2017 Andrew Dawson
|
||||||
|
*
|
||||||
|
* This file is part of Tusky.
|
||||||
|
*
|
||||||
|
* Tusky 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
|
/**A wrapper for android.util.Log that allows for disabling logging, such as for release builds.*/
|
||||||
|
public class Log {
|
||||||
|
private static final boolean LOGGING_ENABLED = BuildConfig.DEBUG;
|
||||||
|
|
||||||
|
public static void i(String tag, String string) {
|
||||||
|
if (LOGGING_ENABLED) {
|
||||||
|
android.util.Log.i(tag, string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void e(String tag, String string) {
|
||||||
|
if (LOGGING_ENABLED) {
|
||||||
|
android.util.Log.e(tag, string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void d(String tag, String string) {
|
||||||
|
if (LOGGING_ENABLED) {
|
||||||
|
android.util.Log.d(tag, string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void v(String tag, String string) {
|
||||||
|
if (LOGGING_ENABLED) {
|
||||||
|
android.util.Log.v(tag, string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void w(String tag, String string) {
|
||||||
|
if (LOGGING_ENABLED) {
|
||||||
|
android.util.Log.w(tag, string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,6 @@ import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
@ -129,6 +128,7 @@ public class LoginActivity extends AppCompatActivity {
|
||||||
parameters.put("client_name", getString(R.string.app_name));
|
parameters.put("client_name", getString(R.string.app_name));
|
||||||
parameters.put("redirect_uris", getOauthRedirectUri());
|
parameters.put("redirect_uris", getOauthRedirectUri());
|
||||||
parameters.put("scopes", OAUTH_SCOPES);
|
parameters.put("scopes", OAUTH_SCOPES);
|
||||||
|
parameters.put("website", getString(R.string.app_website));
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(TAG, "Unable to build the form data for the authentication request.");
|
Log.e(TAG, "Unable to build the form data for the authentication request.");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -27,7 +27,6 @@ import android.support.v4.view.ViewPager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -137,7 +136,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
id = response.getString("id");
|
id = response.getString("id");
|
||||||
username = response.getString("acct");
|
username = response.getString("acct");
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
onFetchUserInfoFailure();
|
onFetchUserInfoFailure(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onFetchUserInfoSuccess(id, username);
|
onFetchUserInfoSuccess(id, username);
|
||||||
|
@ -146,7 +145,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
new Response.ErrorListener() {
|
new Response.ErrorListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onErrorResponse(VolleyError error) {
|
public void onErrorResponse(VolleyError error) {
|
||||||
onFetchUserInfoFailure();
|
onFetchUserInfoFailure(error);
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -171,9 +170,9 @@ public class MainActivity extends AppCompatActivity {
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFetchUserInfoFailure() {
|
private void onFetchUserInfoFailure(Exception exception) {
|
||||||
//TODO: help
|
//TODO: help
|
||||||
Log.e(TAG, "Failed to fetch the logged-in user's info.");
|
Log.e(TAG, "Failed to fetch user info. " + exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @Unused: For Firebase push notifications, useless for now.
|
/* @Unused: For Firebase push notifications, useless for now.
|
||||||
|
|
|
@ -42,6 +42,8 @@ import java.util.Map;
|
||||||
|
|
||||||
public class NotificationsFragment extends SFragment implements
|
public class NotificationsFragment extends SFragment implements
|
||||||
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, FooterActionListener {
|
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, FooterActionListener {
|
||||||
|
private static final String TAG = "Notifications"; // logging tag
|
||||||
|
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private NotificationsAdapter adapter;
|
private NotificationsAdapter adapter;
|
||||||
|
@ -108,13 +110,13 @@ public class NotificationsFragment extends SFragment implements
|
||||||
List<Notification> notifications = Notification.parse(response);
|
List<Notification> notifications = Notification.parse(response);
|
||||||
onFetchNotificationsSuccess(notifications, fromId != null);
|
onFetchNotificationsSuccess(notifications, fromId != null);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
onFetchNotificationsFailure();
|
onFetchNotificationsFailure(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, new Response.ErrorListener() {
|
}, new Response.ErrorListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onErrorResponse(VolleyError error) {
|
public void onErrorResponse(VolleyError error) {
|
||||||
onFetchNotificationsFailure();
|
onFetchNotificationsFailure(error);
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -141,9 +143,10 @@ public class NotificationsFragment extends SFragment implements
|
||||||
swipeRefreshLayout.setRefreshing(false);
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFetchNotificationsFailure() {
|
private void onFetchNotificationsFailure(Exception exception) {
|
||||||
showFetchTimelineRetry(true);
|
showFetchTimelineRetry(true);
|
||||||
swipeRefreshLayout.setRefreshing(false);
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
Log.e(TAG, "Fetch failure: " + exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showFetchTimelineRetry(boolean show) {
|
private void showFetchTimelineRetry(boolean show) {
|
||||||
|
|
|
@ -26,7 +26,6 @@ import android.provider.Settings;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.app.TaskStackBuilder;
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.volley.AuthFailureError;
|
import com.android.volley.AuthFailureError;
|
||||||
import com.android.volley.Response;
|
import com.android.volley.Response;
|
||||||
|
@ -44,7 +43,8 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class PullNotificationService extends IntentService {
|
public class PullNotificationService extends IntentService {
|
||||||
private final int NOTIFY_ID = 6; // This is an arbitrary number.
|
private static final int NOTIFY_ID = 6; // This is an arbitrary number.
|
||||||
|
private static final String TAG = "PullNotifications";
|
||||||
|
|
||||||
public PullNotificationService() {
|
public PullNotificationService() {
|
||||||
super("Tusky Pull Notification Service");
|
super("Tusky Pull Notification Service");
|
||||||
|
@ -76,7 +76,7 @@ public class PullNotificationService extends IntentService {
|
||||||
try {
|
try {
|
||||||
notifications = Notification.parse(response);
|
notifications = Notification.parse(response);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
onCheckNotificationsFailure();
|
onCheckNotificationsFailure(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onCheckNotificationsSuccess(notifications, lastUpdate);
|
onCheckNotificationsSuccess(notifications, lastUpdate);
|
||||||
|
@ -84,7 +84,7 @@ public class PullNotificationService extends IntentService {
|
||||||
}, new Response.ErrorListener() {
|
}, new Response.ErrorListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onErrorResponse(VolleyError error) {
|
public void onErrorResponse(VolleyError error) {
|
||||||
onCheckNotificationsFailure();
|
onCheckNotificationsFailure(error);
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -134,9 +134,9 @@ public class PullNotificationService extends IntentService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCheckNotificationsFailure() {
|
private void onCheckNotificationsFailure(Exception exception) {
|
||||||
//TODO: not sure if just logging here is enough?
|
//TODO: not sure if just logging here is enough?
|
||||||
Log.e("Error", "Could not check notifications in the service.");
|
Log.e(TAG, "Failed to check notifications. " + exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MentionResult {
|
private static class MentionResult {
|
||||||
|
@ -193,7 +193,7 @@ public class PullNotificationService extends IntentService {
|
||||||
if (preferences.getBoolean("notificationStyleVibrate", false)) {
|
if (preferences.getBoolean("notificationStyleVibrate", false)) {
|
||||||
builder.setVibrate(new long[] { 500, 500 });
|
builder.setVibrate(new long[] { 500, 500 });
|
||||||
}
|
}
|
||||||
if (preferences.getBoolean("notificationStyleLight", true)) {
|
if (preferences.getBoolean("notificationStyleLight", false)) {
|
||||||
builder.setLights(0xFF00FF8F, 300, 1000);
|
builder.setLights(0xFF00FF8F, 300, 1000);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < mentions.size(); i++) {
|
for (int i = 0; i < mentions.size(); i++) {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v7.widget.PopupMenu;
|
import android.support.v7.widget.PopupMenu;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
import android.support.v7.widget.DividerItemDecoration;
|
import android.support.v7.widget.DividerItemDecoration;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 5.6 KiB |
|
@ -1,5 +1,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Tusky</string>
|
<string name="app_name">Tusky</string>
|
||||||
|
<string name="app_website">http://tusky.keylesspalace.com</string>
|
||||||
|
|
||||||
<string name="oauth_scheme">com.keylesspalace.tusky</string>
|
<string name="oauth_scheme">com.keylesspalace.tusky</string>
|
||||||
<string name="oauth_redirect_host">oauth2redirect</string>
|
<string name="oauth_redirect_host">oauth2redirect</string>
|
||||||
|
@ -112,6 +113,8 @@
|
||||||
websites.\n\nSo, find the address of one you\'d like to join and enter it here. This will
|
websites.\n\nSo, find the address of one you\'d like to join and enter it here. This will
|
||||||
direct you there to either make an account or log in.
|
direct you there to either make an account or log in.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="dialog_title_finishing_media_upload">Finishing Media Upload</string>
|
||||||
|
<string name="dialog_message_uploading_media">Uploading…</string>
|
||||||
|
|
||||||
<string name="visibility_public">Show on public timeline</string>
|
<string name="visibility_public">Show on public timeline</string>
|
||||||
<string name="visibility_unlisted">Do not display on public timeline</string>
|
<string name="visibility_unlisted">Do not display on public timeline</string>
|
||||||
|
|