commit
1b3ef5d87e
|
@ -7,9 +7,8 @@ android {
|
||||||
applicationId "fr.gouv.etalab.mastodon"
|
applicationId "fr.gouv.etalab.mastodon"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 5
|
versionCode 7
|
||||||
versionName "1.0.5"
|
versionName "1.1.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
@ -21,17 +20,11 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
|
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
|
||||||
})
|
|
||||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||||
compile 'com.android.support:design:25.3.1'
|
compile 'com.android.support:design:25.3.1'
|
||||||
compile 'com.android.support:support-v4:25.3.1'
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4'
|
|
||||||
compile 'com.loopj.android:android-async-http:1.4.9'
|
compile 'com.loopj.android:android-async-http:1.4.9'
|
||||||
compile 'com.google.code.gson:gson:2.8.0'
|
compile 'com.google.code.gson:gson:2.8.0'
|
||||||
compile 'com.squareup.retrofit2:retrofit:2.2.0'
|
|
||||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
compile 'com.evernote:android-job:1.1.9'
|
compile 'com.evernote:android-job:1.1.10'
|
||||||
testCompile 'junit:junit:4.12'
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,10 +42,6 @@
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:theme="@style/AppTheme.NoActionBar">
|
android:theme="@style/AppTheme.NoActionBar">
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="fr.gouv.etalab.mastodon.activities.WebviewActivity"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
/>
|
|
||||||
<activity android:name="fr.gouv.etalab.mastodon.activities.LoginActivity"
|
<activity android:name="fr.gouv.etalab.mastodon.activities.LoginActivity"
|
||||||
android:windowSoftInputMode="stateAlwaysHidden"
|
android:windowSoftInputMode="stateAlwaysHidden"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
|
@ -66,7 +62,6 @@
|
||||||
<activity android:name="fr.gouv.etalab.mastodon.activities.AboutActivity"
|
<activity android:name="fr.gouv.etalab.mastodon.activities.AboutActivity"
|
||||||
android:windowSoftInputMode="stateAlwaysHidden"
|
android:windowSoftInputMode="stateAlwaysHidden"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:noHistory="true"
|
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
/>
|
/>
|
||||||
<activity android:name="fr.gouv.etalab.mastodon.activities.TootActivity"
|
<activity android:name="fr.gouv.etalab.mastodon.activities.TootActivity"
|
||||||
|
|
|
@ -14,15 +14,15 @@
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
package fr.gouv.etalab.mastodon.activities;
|
package fr.gouv.etalab.mastodon.activities;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Color;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.text.Html;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import mastodon.etalab.gouv.fr.mastodon.R;
|
import mastodon.etalab.gouv.fr.mastodon.R;
|
||||||
|
@ -48,24 +48,34 @@ public class AboutActivity extends AppCompatActivity {
|
||||||
about_version.setText(getResources().getString(R.string.about_vesrion, version));
|
about_version.setText(getResources().getString(R.string.about_vesrion, version));
|
||||||
} catch (PackageManager.NameNotFoundException ignored) {}
|
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||||
|
|
||||||
TextView about_developer = (TextView) findViewById(R.id.about_developer);
|
Button about_developer = (Button) findViewById(R.id.about_developer);
|
||||||
TextView about_license = (TextView) findViewById(R.id.about_license);
|
Button about_code = (Button) findViewById(R.id.about_code);
|
||||||
TextView about_code = (TextView) findViewById(R.id.about_code);
|
Button about_license = (Button) findViewById(R.id.about_license);
|
||||||
about_developer.setMovementMethod(LinkMovementMethod.getInstance());
|
|
||||||
about_license.setMovementMethod(LinkMovementMethod.getInstance());
|
about_code.setOnClickListener(new View.OnClickListener() {
|
||||||
about_code.setMovementMethod(LinkMovementMethod.getInstance());
|
@Override
|
||||||
about_developer.setLinkTextColor(Color.BLUE);
|
public void onClick(View v) {
|
||||||
about_license.setLinkTextColor(Color.BLUE);
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://bitbucket.org/tom79/mastodon_etalab"));
|
||||||
about_code.setLinkTextColor(Color.BLUE);
|
startActivity(browserIntent);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
}
|
||||||
about_developer.setText(Html.fromHtml(getString(R.string.about_developer), Html.FROM_HTML_MODE_COMPACT));
|
});
|
||||||
about_license.setText(Html.fromHtml(getString(R.string.about_license), Html.FROM_HTML_MODE_COMPACT));
|
about_developer.setOnClickListener(new View.OnClickListener() {
|
||||||
about_code.setText(Html.fromHtml(getString(R.string.about_code), Html.FROM_HTML_MODE_COMPACT));
|
@Override
|
||||||
}else {
|
public void onClick(View v) {
|
||||||
about_developer.setText(Html.fromHtml(getString(R.string.about_developer)));
|
Intent intent = new Intent(AboutActivity.this, ShowAccountActivity.class);
|
||||||
about_license.setText(Html.fromHtml(getString(R.string.about_license)));
|
Bundle b = new Bundle();
|
||||||
about_code.setText(Html.fromHtml(getString(R.string.about_code)));
|
b.putString("accountId", "2416");
|
||||||
}
|
intent.putExtras(b);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
about_license.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.gnu.org/licenses/quick-guide-gplv3.fr.html"));
|
||||||
|
startActivity(browserIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,26 +15,37 @@
|
||||||
package fr.gouv.etalab.mastodon.activities;
|
package fr.gouv.etalab.mastodon.activities;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.loopj.android.http.AsyncHttpClient;
|
||||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||||
|
import com.loopj.android.http.RequestParams;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import cz.msebera.android.httpclient.Header;
|
import cz.msebera.android.httpclient.Header;
|
||||||
|
import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoAsyncTask;
|
||||||
|
import fr.gouv.etalab.mastodon.client.MastalabSSLSocketFactory;
|
||||||
import fr.gouv.etalab.mastodon.client.OauthClient;
|
import fr.gouv.etalab.mastodon.client.OauthClient;
|
||||||
import fr.gouv.etalab.mastodon.helper.Helper;
|
import fr.gouv.etalab.mastodon.helper.Helper;
|
||||||
import mastodon.etalab.gouv.fr.mastodon.R;
|
import mastodon.etalab.gouv.fr.mastodon.R;
|
||||||
|
|
||||||
|
import static fr.gouv.etalab.mastodon.helper.Helper.USER_AGENT;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Thomas on 23/04/2017.
|
* Created by Thomas on 23/04/2017.
|
||||||
|
@ -49,14 +60,23 @@ public class LoginActivity extends AppCompatActivity {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_login);
|
setContentView(R.layout.activity_login);
|
||||||
|
|
||||||
final Button connectionButton = (Button) findViewById(R.id.login_button);
|
Button connectionButton = (Button) findViewById(R.id.login_button);
|
||||||
final Intent webviewIntent = new Intent(this, WebviewActivity.class);
|
|
||||||
|
|
||||||
|
|
||||||
connectionButton.setEnabled(false);
|
connectionButton.setEnabled(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume(){
|
||||||
|
super.onResume();
|
||||||
|
Button connectionButton = (Button) findViewById(R.id.login_button);
|
||||||
|
if( !connectionButton.isEnabled())
|
||||||
|
retrievesClientId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void retrievesClientId(){
|
||||||
|
final Button connectionButton = (Button) findViewById(R.id.login_button);
|
||||||
String action = "/api/v1/apps";
|
String action = "/api/v1/apps";
|
||||||
HashMap<String,String> parameters = new HashMap<>();
|
final HashMap<String,String> parameters = new HashMap<>();
|
||||||
parameters.put(Helper.CLIENT_NAME, Helper.OAUTH_REDIRECT_HOST);
|
parameters.put(Helper.CLIENT_NAME, Helper.OAUTH_REDIRECT_HOST);
|
||||||
parameters.put(Helper.REDIRECT_URIS,"https://" + Helper.INSTANCE + Helper.REDIRECT_CONTENT);
|
parameters.put(Helper.REDIRECT_URIS,"https://" + Helper.INSTANCE + Helper.REDIRECT_CONTENT);
|
||||||
parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES);
|
parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES);
|
||||||
|
@ -95,14 +115,54 @@ public class LoginActivity extends AppCompatActivity {
|
||||||
connectionButton.setOnClickListener(new View.OnClickListener() {
|
connectionButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
startActivity(webviewIntent);
|
connectionButton.setEnabled(false);
|
||||||
finish();
|
AsyncHttpClient client = new AsyncHttpClient();
|
||||||
|
RequestParams requestParams = new RequestParams();
|
||||||
|
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
requestParams.add(Helper.CLIENT_ID, sharedpreferences.getString(Helper.CLIENT_ID, null));
|
||||||
|
requestParams.add(Helper.CLIENT_SECRET, sharedpreferences.getString(Helper.CLIENT_SECRET, null));
|
||||||
|
requestParams.add("grant_type", "password");
|
||||||
|
EditText login_uid = (EditText) findViewById(R.id.login_uid);
|
||||||
|
EditText login_passwd = (EditText) findViewById(R.id.login_passwd);
|
||||||
|
requestParams.add("username",login_uid.getText().toString().trim());
|
||||||
|
requestParams.add("password",login_passwd.getText().toString().trim());
|
||||||
|
client.setUserAgent(USER_AGENT);
|
||||||
|
try {
|
||||||
|
client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore()));
|
||||||
|
client.post("https://" + Helper.INSTANCE + "/oauth/token", requestParams, new AsyncHttpResponseHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
|
||||||
|
String response = new String(responseBody);
|
||||||
|
JSONObject resobj;
|
||||||
|
try {
|
||||||
|
resobj = new JSONObject(response);
|
||||||
|
String token = resobj.get("access_token").toString();
|
||||||
|
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
|
||||||
|
editor.apply();
|
||||||
|
//Update the account with the token;
|
||||||
|
new UpdateAccountInfoAsyncTask(LoginActivity.this, token).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
|
||||||
|
connectionButton.setEnabled(true);
|
||||||
|
Toast.makeText(getApplicationContext(),R.string.toast_error_login,Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException | KeyStoreException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import java.util.HashMap;
|
||||||
|
|
||||||
import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoByIDAsyncTask;
|
import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoByIDAsyncTask;
|
||||||
import fr.gouv.etalab.mastodon.client.Entities.Account;
|
import fr.gouv.etalab.mastodon.client.Entities.Account;
|
||||||
|
import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader;
|
||||||
import fr.gouv.etalab.mastodon.fragments.DisplayAccountsFragment;
|
import fr.gouv.etalab.mastodon.fragments.DisplayAccountsFragment;
|
||||||
import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment;
|
import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment;
|
||||||
import fr.gouv.etalab.mastodon.helper.Helper;
|
import fr.gouv.etalab.mastodon.helper.Helper;
|
||||||
|
@ -62,8 +63,9 @@ import fr.gouv.etalab.mastodon.fragments.TabLayoutSettingsFragment;
|
||||||
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
|
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
|
||||||
import mastodon.etalab.gouv.fr.mastodon.R;
|
import mastodon.etalab.gouv.fr.mastodon.R;
|
||||||
|
|
||||||
|
import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT;
|
||||||
import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION;
|
import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION;
|
||||||
import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_NOTIFICATION;
|
import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity
|
public class MainActivity extends AppCompatActivity
|
||||||
implements NavigationView.OnNavigationItemSelectedListener, OnUpdateAccountInfoInterface {
|
implements NavigationView.OnNavigationItemSelectedListener, OnUpdateAccountInfoInterface {
|
||||||
|
@ -119,6 +121,7 @@ public class MainActivity extends AppCompatActivity
|
||||||
File cacheDir = new File(getCacheDir(), getString(R.string.app_name));
|
File cacheDir = new File(getCacheDir(), getString(R.string.app_name));
|
||||||
ImageLoaderConfiguration configImg = new ImageLoaderConfiguration.Builder(this)
|
ImageLoaderConfiguration configImg = new ImageLoaderConfiguration.Builder(this)
|
||||||
.threadPoolSize(5)
|
.threadPoolSize(5)
|
||||||
|
.imageDownloader(new PatchBaseImageDownloader(getApplicationContext()))
|
||||||
.threadPriority(Thread.MIN_PRIORITY + 3)
|
.threadPriority(Thread.MIN_PRIORITY + 3)
|
||||||
.denyCacheImageMultipleSizesInMemory()
|
.denyCacheImageMultipleSizesInMemory()
|
||||||
.diskCache(new UnlimitedDiskCache(cacheDir))
|
.diskCache(new UnlimitedDiskCache(cacheDir))
|
||||||
|
@ -138,10 +141,14 @@ public class MainActivity extends AppCompatActivity
|
||||||
boolean menuWasSelected = false;
|
boolean menuWasSelected = false;
|
||||||
if( getIntent() != null && getIntent().getExtras() != null ){
|
if( getIntent() != null && getIntent().getExtras() != null ){
|
||||||
Bundle extras = getIntent().getExtras();
|
Bundle extras = getIntent().getExtras();
|
||||||
if (extras.getInt(INTENT_ACTION) == INTENT_NOTIFICATION){
|
if (extras.getInt(INTENT_ACTION) == NOTIFICATION_INTENT){
|
||||||
navigationView.setCheckedItem(R.id.nav_notification);
|
navigationView.setCheckedItem(R.id.nav_notification);
|
||||||
navigationView.getMenu().performIdentifierAction(R.id.nav_notification, 0);
|
navigationView.getMenu().performIdentifierAction(R.id.nav_notification, 0);
|
||||||
menuWasSelected = true;
|
menuWasSelected = true;
|
||||||
|
}else if( extras.getInt(INTENT_ACTION) == HOME_TIMELINE_INTENT){
|
||||||
|
navigationView.setCheckedItem(R.id.nav_home);
|
||||||
|
navigationView.getMenu().performIdentifierAction(R.id.nav_home, 0);
|
||||||
|
menuWasSelected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (savedInstanceState == null && !menuWasSelected) {
|
if (savedInstanceState == null && !menuWasSelected) {
|
||||||
|
@ -190,10 +197,13 @@ public class MainActivity extends AppCompatActivity
|
||||||
return;
|
return;
|
||||||
Bundle extras = intent.getExtras();
|
Bundle extras = intent.getExtras();
|
||||||
if( extras.containsKey(INTENT_ACTION) ){
|
if( extras.containsKey(INTENT_ACTION) ){
|
||||||
if (extras.getInt(INTENT_ACTION) == INTENT_NOTIFICATION){
|
final NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
|
||||||
final NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
|
if (extras.getInt(INTENT_ACTION) == NOTIFICATION_INTENT){
|
||||||
navigationView.setCheckedItem(R.id.nav_notification);
|
navigationView.setCheckedItem(R.id.nav_notification);
|
||||||
navigationView.getMenu().performIdentifierAction(R.id.nav_notification, 0);
|
navigationView.getMenu().performIdentifierAction(R.id.nav_notification, 0);
|
||||||
|
}else if( extras.getInt(INTENT_ACTION) == HOME_TIMELINE_INTENT){
|
||||||
|
navigationView.setCheckedItem(R.id.nav_home);
|
||||||
|
navigationView.getMenu().performIdentifierAction(R.id.nav_home, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
intent.replaceExtras(new Bundle());
|
intent.replaceExtras(new Bundle());
|
||||||
|
|
|
@ -18,10 +18,12 @@ import android.app.Application;
|
||||||
import com.evernote.android.job.JobManager;
|
import com.evernote.android.job.JobManager;
|
||||||
|
|
||||||
import fr.gouv.etalab.mastodon.jobs.ApplicationJob;
|
import fr.gouv.etalab.mastodon.jobs.ApplicationJob;
|
||||||
|
import fr.gouv.etalab.mastodon.jobs.HomeTimelineSyncJob;
|
||||||
import fr.gouv.etalab.mastodon.jobs.NotificationsSyncJob;
|
import fr.gouv.etalab.mastodon.jobs.NotificationsSyncJob;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Thomas on 29/04/2017.
|
* Created by Thomas on 29/04/2017.
|
||||||
|
* Main application, jobs are launched here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class MainApplication extends Application{
|
public class MainApplication extends Application{
|
||||||
|
@ -32,6 +34,7 @@ public class MainApplication extends Application{
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
JobManager.create(this).addJobCreator(new ApplicationJob());
|
JobManager.create(this).addJobCreator(new ApplicationJob());
|
||||||
JobManager.instance().getConfig().setVerbose(false);
|
JobManager.instance().getConfig().setVerbose(false);
|
||||||
NotificationsSyncJob.schedule(getApplicationContext(), false);
|
NotificationsSyncJob.schedule(false);
|
||||||
|
HomeTimelineSyncJob.schedule(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,21 +14,28 @@
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
package fr.gouv.etalab.mastodon.activities;
|
package fr.gouv.etalab.mastodon.activities;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.TabLayout;
|
import android.support.design.widget.TabLayout;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v4.view.PagerAdapter;
|
import android.support.v4.view.PagerAdapter;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.text.Html;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
@ -77,8 +84,9 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
|
||||||
private ViewPager mPager;
|
private ViewPager mPager;
|
||||||
private String accountId;
|
private String accountId;
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
|
private BroadcastReceiver hide_header;
|
||||||
|
private TextView account_note;
|
||||||
|
private String userId;
|
||||||
|
|
||||||
public enum action{
|
public enum action{
|
||||||
FOLLOW,
|
FOLLOW,
|
||||||
|
@ -94,6 +102,9 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_show_account);
|
setContentView(R.layout.activity_show_account);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
imageLoader = ImageLoader.getInstance();
|
imageLoader = ImageLoader.getInstance();
|
||||||
statuses = new ArrayList<>();
|
statuses = new ArrayList<>();
|
||||||
|
@ -109,7 +120,7 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
|
||||||
accountId = b.getString("accountId");
|
accountId = b.getString("accountId");
|
||||||
new RetrieveRelationshipAsyncTask(getApplicationContext(), accountId,ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
new RetrieveRelationshipAsyncTask(getApplicationContext(), accountId,ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
new RetrieveAccountAsyncTask(getApplicationContext(),accountId, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
new RetrieveAccountAsyncTask(getApplicationContext(),accountId, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
||||||
if( accountId != null && accountId.equals(userId)){
|
if( accountId != null && accountId.equals(userId)){
|
||||||
account_follow.setVisibility(View.GONE);
|
account_follow.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
@ -164,6 +175,30 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
account_note = (TextView) findViewById(R.id.account_note);
|
||||||
|
//Register LocalBroadcast to receive selected accounts after search
|
||||||
|
hide_header = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
boolean hide = intent.getBooleanExtra("hide", false);
|
||||||
|
if( hide){
|
||||||
|
account_follow.setVisibility(View.GONE);
|
||||||
|
account_note.setVisibility(View.GONE);
|
||||||
|
tabLayout.setVisibility(View.GONE);
|
||||||
|
}else {
|
||||||
|
account_follow.setVisibility(View.VISIBLE);
|
||||||
|
if( accountId != null && accountId.equals(userId)){
|
||||||
|
account_follow.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
tabLayout.setVisibility(View.VISIBLE);
|
||||||
|
account_note.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(hide_header, new IntentFilter(Helper.HEADER_ACCOUNT));
|
||||||
|
|
||||||
//Follow button
|
//Follow button
|
||||||
account_follow.setOnClickListener(new View.OnClickListener() {
|
account_follow.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -215,9 +250,14 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
|
||||||
account_ac.setVisibility(View.GONE);
|
account_ac.setVisibility(View.GONE);
|
||||||
else
|
else
|
||||||
account_ac.setText(account.getAcct());
|
account_ac.setText(account.getAcct());
|
||||||
tabLayout.getTabAt(0).setText(getString(R.string.status) + "\n" + String.valueOf(account.getStatuses_count()));
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
tabLayout.getTabAt(1).setText(getString(R.string.following) + "\n" + String.valueOf(account.getFollowing_count()));
|
account_note.setText(Html.fromHtml(account.getNote(), Html.FROM_HTML_MODE_COMPACT));
|
||||||
tabLayout.getTabAt(2).setText(getString(R.string.followers) + "\n" + String.valueOf(account.getFollowers_count()));
|
else
|
||||||
|
//noinspection deprecation
|
||||||
|
account_note.setText(Html.fromHtml(account.getNote()));
|
||||||
|
tabLayout.getTabAt(0).setText(getString(R.string.status_cnt, account.getStatuses_count()));
|
||||||
|
tabLayout.getTabAt(1).setText(getString(R.string.following_cnt, account.getFollowing_count()));
|
||||||
|
tabLayout.getTabAt(2).setText(getString(R.string.followers_cnt, account.getFollowers_count()));
|
||||||
imageLoader.displayImage(account.getAvatar(), account_pp, options);
|
imageLoader.displayImage(account.getAvatar(), account_pp, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,6 +272,12 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy(){
|
||||||
|
super.onDestroy();
|
||||||
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(hide_header);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRetrieveRelationship(Relationship relationship) {
|
public void onRetrieveRelationship(Relationship relationship) {
|
||||||
if( relationship.isBlocking()){
|
if( relationship.isBlocking()){
|
||||||
|
@ -280,18 +326,21 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
|
||||||
DisplayStatusFragment displayStatusFragment = new DisplayStatusFragment();
|
DisplayStatusFragment displayStatusFragment = new DisplayStatusFragment();
|
||||||
bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.USER);
|
bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.USER);
|
||||||
bundle.putString("targetedId", accountId);
|
bundle.putString("targetedId", accountId);
|
||||||
|
bundle.putBoolean("hideHeader",true);
|
||||||
displayStatusFragment.setArguments(bundle);
|
displayStatusFragment.setArguments(bundle);
|
||||||
return displayStatusFragment;
|
return displayStatusFragment;
|
||||||
case 1:
|
case 1:
|
||||||
DisplayAccountsFragment displayAccountsFragment = new DisplayAccountsFragment();
|
DisplayAccountsFragment displayAccountsFragment = new DisplayAccountsFragment();
|
||||||
bundle.putSerializable("type", RetrieveAccountsAsyncTask.Type.FOLLOWING);
|
bundle.putSerializable("type", RetrieveAccountsAsyncTask.Type.FOLLOWING);
|
||||||
bundle.putString("targetedId", accountId);
|
bundle.putString("targetedId", accountId);
|
||||||
|
bundle.putBoolean("hideHeader",true);
|
||||||
displayAccountsFragment.setArguments(bundle);
|
displayAccountsFragment.setArguments(bundle);
|
||||||
return displayAccountsFragment;
|
return displayAccountsFragment;
|
||||||
case 2:
|
case 2:
|
||||||
displayAccountsFragment = new DisplayAccountsFragment();
|
displayAccountsFragment = new DisplayAccountsFragment();
|
||||||
bundle.putSerializable("type", RetrieveAccountsAsyncTask.Type.FOLLOWERS);
|
bundle.putSerializable("type", RetrieveAccountsAsyncTask.Type.FOLLOWERS);
|
||||||
bundle.putString("targetedId", accountId);
|
bundle.putString("targetedId", accountId);
|
||||||
|
bundle.putBoolean("hideHeader",true);
|
||||||
displayAccountsFragment.setArguments(bundle);
|
displayAccountsFragment.setArguments(bundle);
|
||||||
return displayAccountsFragment;
|
return displayAccountsFragment;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,195 +0,0 @@
|
||||||
/* Copyright 2017 Thomas Schneider
|
|
||||||
*
|
|
||||||
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Mastodon Etalab 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 Thomas Schneider; if not,
|
|
||||||
* see <http://www.gnu.org/licenses>. */
|
|
||||||
|
|
||||||
package fr.gouv.etalab.mastodon.activities;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.webkit.CookieManager;
|
|
||||||
import android.webkit.CookieSyncManager;
|
|
||||||
import android.webkit.WebChromeClient;
|
|
||||||
import android.webkit.WebView;
|
|
||||||
import android.webkit.WebViewClient;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
|
|
||||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import cz.msebera.android.httpclient.Header;
|
|
||||||
import mastodon.etalab.gouv.fr.mastodon.R;
|
|
||||||
import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoAsyncTask;
|
|
||||||
import fr.gouv.etalab.mastodon.client.OauthClient;
|
|
||||||
import fr.gouv.etalab.mastodon.helper.Helper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Thomas on 24/04/2017.
|
|
||||||
* Webview to connect accounts
|
|
||||||
*/
|
|
||||||
public class WebviewActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
|
|
||||||
private Activity activity;
|
|
||||||
private WebView webView;
|
|
||||||
private Context context;
|
|
||||||
private AlertDialog alert;
|
|
||||||
private String clientId, clientSecret;
|
|
||||||
|
|
||||||
public void onCreate(Bundle savedInstanceState)
|
|
||||||
{
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_webview);
|
|
||||||
this.activity = this;
|
|
||||||
this.context = this;
|
|
||||||
this.context = this.getApplicationContext();
|
|
||||||
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
|
||||||
clientId = sharedpreferences.getString(Helper.CLIENT_ID, null);
|
|
||||||
clientSecret = sharedpreferences.getString(Helper.CLIENT_SECRET, null);
|
|
||||||
|
|
||||||
webView = (WebView) findViewById(R.id.webviewConnect);
|
|
||||||
clearCookies(getApplicationContext());
|
|
||||||
final ProgressBar pbar = (ProgressBar) findViewById(R.id.progress_bar);
|
|
||||||
|
|
||||||
webView.setWebChromeClient(new WebChromeClient() {
|
|
||||||
@Override
|
|
||||||
public void onProgressChanged(WebView view, int progress) {
|
|
||||||
if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
|
|
||||||
pbar.setVisibility(ProgressBar.VISIBLE);
|
|
||||||
}
|
|
||||||
pbar.setProgress(progress);
|
|
||||||
if (progress == 100) {
|
|
||||||
pbar.setVisibility(ProgressBar.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
webView.setWebViewClient(new WebViewClient() {
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
|
||||||
public boolean shouldOverrideUrlLoading(WebView view, String url){
|
|
||||||
super.shouldOverrideUrlLoading(view,url);
|
|
||||||
if( url.contains(Helper.REDIRECT_CONTENT)){
|
|
||||||
String val[] = url.split("code=");
|
|
||||||
String code = val[1];
|
|
||||||
|
|
||||||
String action = "/oauth/token";
|
|
||||||
HashMap<String,String> parameters = new HashMap<>();
|
|
||||||
parameters.put(Helper.CLIENT_ID, clientId);
|
|
||||||
parameters.put(Helper.CLIENT_SECRET, clientSecret);
|
|
||||||
parameters.put(Helper.REDIRECT_URI,"https://" + Helper.INSTANCE + Helper.REDIRECT_CONTENT);
|
|
||||||
parameters.put("grant_type", "authorization_code");
|
|
||||||
parameters.put("code",code);
|
|
||||||
new OauthClient().post(action, parameters, new AsyncHttpResponseHandler() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
|
|
||||||
String response = new String(responseBody);
|
|
||||||
JSONObject resobj;
|
|
||||||
try {
|
|
||||||
resobj = new JSONObject(response);
|
|
||||||
String token = resobj.get("access_token").toString();
|
|
||||||
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
|
||||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
|
||||||
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
|
|
||||||
editor.apply();
|
|
||||||
//Update the account with the token;
|
|
||||||
new UpdateAccountInfoAsyncTask(WebviewActivity.this, true, token).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
|
|
||||||
error.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
webView.loadUrl(redirectUserToAuthorizeAndLogin());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
if (webView != null && webView.canGoBack()) {
|
|
||||||
webView.goBack();
|
|
||||||
} else {
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private String redirectUserToAuthorizeAndLogin() {
|
|
||||||
|
|
||||||
String queryString = Helper.CLIENT_ID + "="+ clientId;
|
|
||||||
queryString += "&" + Helper.REDIRECT_URI + "="+ Uri.encode("https://" + Helper.INSTANCE + "/redirect_mastodon_api");
|
|
||||||
queryString += "&" + Helper.RESPONSE_TYPE +"=code";
|
|
||||||
queryString += "&" + Helper.SCOPE +"=" + Helper.OAUTH_SCOPES;
|
|
||||||
/*try {
|
|
||||||
queryString = URLEncoder.encode(queryString, "utf-8");
|
|
||||||
} catch (UnsupportedEncodingException ignored) {}*/
|
|
||||||
return "https://" + Helper.INSTANCE + Helper.EP_AUTHORIZE + "?" + queryString;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String getOauthRedirectUri() {
|
|
||||||
return Helper.OAUTH_SCHEME + "://" + Helper.OAUTH_REDIRECT_HOST + "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
if (alert != null) {
|
|
||||||
alert.dismiss();
|
|
||||||
alert = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public static void clearCookies(Context context)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
||||||
CookieManager.getInstance().removeAllCookies(null);
|
|
||||||
CookieManager.getInstance().flush();
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
CookieSyncManager cookieSyncMngr=CookieSyncManager.createInstance(context);
|
|
||||||
cookieSyncMngr.startSync();
|
|
||||||
CookieManager cookieManager=CookieManager.getInstance();
|
|
||||||
cookieManager.removeAllCookie();
|
|
||||||
cookieManager.removeSessionCookie();
|
|
||||||
cookieSyncMngr.stopSync();
|
|
||||||
cookieSyncMngr.sync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,10 +16,12 @@ package fr.gouv.etalab.mastodon.asynctasks;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import fr.gouv.etalab.mastodon.client.API;
|
import fr.gouv.etalab.mastodon.client.API;
|
||||||
|
import fr.gouv.etalab.mastodon.helper.Helper;
|
||||||
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveFeedsInterface;
|
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveFeedsInterface;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* Copyright 2017 Thomas Schneider
|
||||||
|
*
|
||||||
|
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Mastodon Etalab 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 Thomas Schneider; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
package fr.gouv.etalab.mastodon.asynctasks;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import fr.gouv.etalab.mastodon.client.API;
|
||||||
|
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveHomeTimelineServiceInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Thomas on 20/05/2017.
|
||||||
|
* Retrieves home timeline for the authenticated user - used in the service
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RetrieveHomeTimelineServiceAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private List<fr.gouv.etalab.mastodon.client.Entities.Status> statuses;
|
||||||
|
private String since_id;
|
||||||
|
private String acct;
|
||||||
|
private OnRetrieveHomeTimelineServiceInterface listener;
|
||||||
|
|
||||||
|
|
||||||
|
public RetrieveHomeTimelineServiceAsyncTask(Context context, String since_id, String acct, OnRetrieveHomeTimelineServiceInterface onRetrieveHomeTimelineServiceInterface){
|
||||||
|
this.context = context;
|
||||||
|
this.since_id = since_id;
|
||||||
|
this.listener = onRetrieveHomeTimelineServiceInterface;
|
||||||
|
this.acct = acct;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
statuses = new API(context).getHomeTimelineSinceId(since_id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void result) {
|
||||||
|
listener.onRetrieveHomeTimelineService(statuses, acct);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,13 +20,11 @@ import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import fr.gouv.etalab.mastodon.activities.MainActivity;
|
import fr.gouv.etalab.mastodon.activities.MainActivity;
|
||||||
import fr.gouv.etalab.mastodon.client.API;
|
import fr.gouv.etalab.mastodon.client.API;
|
||||||
import fr.gouv.etalab.mastodon.client.Entities.Account;
|
import fr.gouv.etalab.mastodon.client.Entities.Account;
|
||||||
import fr.gouv.etalab.mastodon.helper.Helper;
|
import fr.gouv.etalab.mastodon.helper.Helper;
|
||||||
import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface;
|
|
||||||
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
|
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
|
||||||
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
|
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
|
||||||
|
|
||||||
|
@ -39,22 +37,11 @@ public class UpdateAccountInfoAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private String token;
|
private String token;
|
||||||
private boolean fromWebview;
|
|
||||||
private boolean error;
|
|
||||||
private OnUpdateAccountInfoInterface listener;
|
|
||||||
|
|
||||||
public UpdateAccountInfoAsyncTask(Context context, String token, OnUpdateAccountInfoInterface onUpdateAccountInfoInterface){
|
|
||||||
|
public UpdateAccountInfoAsyncTask(Context context, String token){
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.fromWebview = false;
|
|
||||||
this.error = false;
|
|
||||||
this.listener = onUpdateAccountInfoInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UpdateAccountInfoAsyncTask(Context context, boolean fromWebview, String token){
|
|
||||||
this.context = context;
|
|
||||||
this.token = token;
|
|
||||||
this.fromWebview = fromWebview;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +65,6 @@ public class UpdateAccountInfoAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||||
else {
|
else {
|
||||||
if( account.getUsername() != null && account.getCreated_at() != null)
|
if( account.getUsername() != null && account.getCreated_at() != null)
|
||||||
new AccountDAO(context, db).insertAccount(account);
|
new AccountDAO(context, db).insertAccount(account);
|
||||||
else //Here the user credential in db doesn't match the remote one (it will be disconnected)
|
|
||||||
error = true;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -87,14 +72,10 @@ public class UpdateAccountInfoAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void result) {
|
protected void onPostExecute(Void result) {
|
||||||
|
|
||||||
if( fromWebview){
|
Intent mainActivity = new Intent(context, MainActivity.class);
|
||||||
Intent mainActivity = new Intent(context, MainActivity.class);
|
mainActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
mainActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
context.startActivity(mainActivity);
|
||||||
context.startActivity(mainActivity);
|
((Activity) context).finish();
|
||||||
((Activity) context).finish();
|
|
||||||
}else{
|
|
||||||
listener.onUpdateAccountInfo(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ package fr.gouv.etalab.mastodon.client;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
import com.loopj.android.http.JsonHttpResponseHandler;
|
||||||
|
@ -29,6 +28,10 @@ import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.*;
|
import java.lang.*;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -42,6 +45,8 @@ import fr.gouv.etalab.mastodon.client.Entities.Notification;
|
||||||
import fr.gouv.etalab.mastodon.client.Entities.Relationship;
|
import fr.gouv.etalab.mastodon.client.Entities.Relationship;
|
||||||
import fr.gouv.etalab.mastodon.client.Entities.Status;
|
import fr.gouv.etalab.mastodon.client.Entities.Status;
|
||||||
|
|
||||||
|
import static fr.gouv.etalab.mastodon.helper.Helper.USER_AGENT;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Thomas on 23/04/2017.
|
* Created by Thomas on 23/04/2017.
|
||||||
|
@ -66,7 +71,6 @@ public class API {
|
||||||
private List<Notification> notifications;
|
private List<Notification> notifications;
|
||||||
private int tootPerPage, accountPerPage, notificationPerPage;
|
private int tootPerPage, accountPerPage, notificationPerPage;
|
||||||
private int actionCode;
|
private int actionCode;
|
||||||
private String userId;
|
|
||||||
|
|
||||||
public enum StatusAction{
|
public enum StatusAction{
|
||||||
FAVOURITE,
|
FAVOURITE,
|
||||||
|
@ -90,7 +94,6 @@ public class API {
|
||||||
tootPerPage = sharedpreferences.getInt(Helper.SET_TOOTS_PER_PAGE, 40);
|
tootPerPage = sharedpreferences.getInt(Helper.SET_TOOTS_PER_PAGE, 40);
|
||||||
accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40);
|
accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40);
|
||||||
notificationPerPage = sharedpreferences.getInt(Helper.SET_NOTIFICATIONS_PER_PAGE, 40);
|
notificationPerPage = sharedpreferences.getInt(Helper.SET_NOTIFICATIONS_PER_PAGE, 40);
|
||||||
userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -296,13 +299,7 @@ public class API {
|
||||||
return statusContext;
|
return statusContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves home timeline for the account *synchronously*
|
|
||||||
* @return List<Status>
|
|
||||||
*/
|
|
||||||
public List<Status> getHomeTimeline() {
|
|
||||||
return getHomeTimeline(null, null, tootPerPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves home timeline for the account *synchronously*
|
* Retrieves home timeline for the account *synchronously*
|
||||||
|
@ -313,6 +310,14 @@ public class API {
|
||||||
return getHomeTimeline(max_id, null, tootPerPage);
|
return getHomeTimeline(max_id, null, tootPerPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves home timeline for the account since an Id value *synchronously*
|
||||||
|
* @return List<Status>
|
||||||
|
*/
|
||||||
|
public List<Status> getHomeTimelineSinceId(String since_id) {
|
||||||
|
return getHomeTimeline(null, since_id, tootPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves home timeline for the account *synchronously*
|
* Retrieves home timeline for the account *synchronously*
|
||||||
* @param max_id String id max
|
* @param max_id String id max
|
||||||
|
@ -331,8 +336,7 @@ public class API {
|
||||||
limit = 40;
|
limit = 40;
|
||||||
params.put("limit",String.valueOf(limit));
|
params.put("limit",String.valueOf(limit));
|
||||||
statuses = new ArrayList<>();
|
statuses = new ArrayList<>();
|
||||||
|
get("/timelines/home", params, new JsonHttpResponseHandler() {
|
||||||
get(String.format("/accounts/%s/statuses",userId), params, new JsonHttpResponseHandler() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
|
@ -351,14 +355,6 @@ public class API {
|
||||||
return statuses;
|
return statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves public timeline for the account *synchronously*
|
|
||||||
* @param local boolean only local timeline
|
|
||||||
* @return List<Status>
|
|
||||||
*/
|
|
||||||
public List<Status> getPublicTimeline(boolean local){
|
|
||||||
return getPublicTimeline(local, null, null, tootPerPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves public timeline for the account *synchronously*
|
* Retrieves public timeline for the account *synchronously*
|
||||||
|
@ -669,10 +665,8 @@ public class API {
|
||||||
if( status.getIn_reply_to_id() != null)
|
if( status.getIn_reply_to_id() != null)
|
||||||
params.put("in_reply_to_id", status.getIn_reply_to_id());
|
params.put("in_reply_to_id", status.getIn_reply_to_id());
|
||||||
if( status.getMedia_attachments() != null && status.getMedia_attachments().size() > 0 ) {
|
if( status.getMedia_attachments() != null && status.getMedia_attachments().size() > 0 ) {
|
||||||
int i = 0;
|
|
||||||
for(Attachment attachment: status.getMedia_attachments()) {
|
for(Attachment attachment: status.getMedia_attachments()) {
|
||||||
params.add("media_ids[]", attachment.getId());
|
params.add("media_ids[]", attachment.getId());
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( status.isSensitive())
|
if( status.isSensitive())
|
||||||
|
@ -703,7 +697,7 @@ public class API {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
delete(action, params, new JsonHttpResponseHandler() {
|
delete(action, null, new JsonHttpResponseHandler() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
|
@ -1162,25 +1156,43 @@ public class API {
|
||||||
|
|
||||||
|
|
||||||
private void get(String action, RequestParams params, AsyncHttpResponseHandler responseHandler) {
|
private void get(String action, RequestParams params, AsyncHttpResponseHandler responseHandler) {
|
||||||
client.setTimeout(5000);
|
client.setTimeout(10000);
|
||||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
|
String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
|
||||||
client.addHeader("Authorization", "Bearer "+prefKeyOauthTokenT);
|
client.addHeader("Authorization", "Bearer "+prefKeyOauthTokenT);
|
||||||
client.get(getAbsoluteUrl(action), params, responseHandler);
|
try {
|
||||||
|
client.setUserAgent(USER_AGENT);
|
||||||
|
client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore()));
|
||||||
|
client.get(getAbsoluteUrl(action), params, responseHandler);
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void post(String action, RequestParams params, AsyncHttpResponseHandler responseHandler) {
|
private void post(String action, RequestParams params, AsyncHttpResponseHandler responseHandler) {
|
||||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
|
String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
|
||||||
client.addHeader("Authorization", "Bearer "+prefKeyOauthTokenT);
|
client.addHeader("Authorization", "Bearer "+prefKeyOauthTokenT);
|
||||||
client.post(getAbsoluteUrl(action), params, responseHandler);
|
try {
|
||||||
|
client.setUserAgent(USER_AGENT);
|
||||||
|
client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore()));
|
||||||
|
client.post(getAbsoluteUrl(action), params, responseHandler);
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void delete(String action, RequestParams params, AsyncHttpResponseHandler responseHandler){
|
private void delete(String action, RequestParams params, AsyncHttpResponseHandler responseHandler){
|
||||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
|
String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
|
||||||
client.addHeader("Authorization", "Bearer "+prefKeyOauthTokenT);
|
client.addHeader("Authorization", "Bearer "+prefKeyOauthTokenT);
|
||||||
client.delete(getAbsoluteUrl(action), params, responseHandler);
|
try {
|
||||||
|
client.setUserAgent(USER_AGENT);
|
||||||
|
client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore()));
|
||||||
|
client.delete(getAbsoluteUrl(action), params, responseHandler);
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAbsoluteUrl(String action) {
|
private String getAbsoluteUrl(String action) {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package fr.gouv.etalab.mastodon.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Thomas on 20/05/2017.
|
||||||
|
* Custom MySSLSocketFactory
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is introduced to fix HTTPS Post bug on API < ICS see
|
||||||
|
* https://code.google.com/p/android/issues/detail?id=13117#c14 <p> </p> Warning! This omits SSL
|
||||||
|
* certificate validation on every device, use with caution
|
||||||
|
*/
|
||||||
|
public class MastalabSSLSocketFactory extends com.loopj.android.http.MySSLSocketFactory {
|
||||||
|
|
||||||
|
private final SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
/**
|
||||||
|
* Creates a new SSL Socket Factory with the given KeyStore.
|
||||||
|
*
|
||||||
|
* @param truststore A KeyStore to create the SSL Socket Factory in context of
|
||||||
|
* @throws NoSuchAlgorithmException NoSuchAlgorithmException
|
||||||
|
* @throws KeyManagementException KeyManagementException
|
||||||
|
* @throws KeyStoreException KeyStoreException
|
||||||
|
* @throws UnrecoverableKeyException UnrecoverableKeyException
|
||||||
|
*/
|
||||||
|
public MastalabSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||||
|
super(truststore);
|
||||||
|
X509TrustManager tm = new X509TrustManager() {
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sslContext.init(null, new TrustManager[]{tm}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(socket, host, port, autoClose));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket() throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Socket enableTLSOnSocket(Socket socket) {
|
||||||
|
if(socket != null && (socket instanceof SSLSocket)) {
|
||||||
|
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
|
||||||
|
}
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,16 +14,24 @@
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
package fr.gouv.etalab.mastodon.client;
|
package fr.gouv.etalab.mastodon.client;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
import com.loopj.android.http.AsyncHttpClient;
|
import com.loopj.android.http.AsyncHttpClient;
|
||||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||||
import com.loopj.android.http.RequestParams;
|
import com.loopj.android.http.RequestParams;
|
||||||
|
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import fr.gouv.etalab.mastodon.helper.Helper;
|
import fr.gouv.etalab.mastodon.helper.Helper;
|
||||||
|
|
||||||
|
import static fr.gouv.etalab.mastodon.helper.Helper.USER_AGENT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Thomas on 23/04/2017.
|
* Created by Thomas on 23/04/2017.
|
||||||
* Client to call urls
|
* Client to call urls
|
||||||
|
@ -37,13 +45,27 @@ public class OauthClient {
|
||||||
|
|
||||||
public void get(String action, HashMap<String, String> paramaters, AsyncHttpResponseHandler responseHandler) {
|
public void get(String action, HashMap<String, String> paramaters, AsyncHttpResponseHandler responseHandler) {
|
||||||
client.setTimeout(5000);
|
client.setTimeout(5000);
|
||||||
|
client.setUserAgent(USER_AGENT);
|
||||||
RequestParams params = hashToRequestParams(paramaters);
|
RequestParams params = hashToRequestParams(paramaters);
|
||||||
|
try {
|
||||||
|
client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore()));
|
||||||
|
client.post(getAbsoluteUrl(action), params, responseHandler);
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
client.get(getAbsoluteUrl(action), params, responseHandler);
|
client.get(getAbsoluteUrl(action), params, responseHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void post(String action, HashMap<String, String> paramaters, AsyncHttpResponseHandler responseHandler) {
|
public void post(String action, HashMap<String, String> paramaters, AsyncHttpResponseHandler responseHandler) {
|
||||||
RequestParams params = hashToRequestParams(paramaters);
|
RequestParams params = hashToRequestParams(paramaters);
|
||||||
client.post(getAbsoluteUrl(action), params, responseHandler);
|
try {
|
||||||
|
client.setConnectTimeout(30000); //30s timeout
|
||||||
|
client.setUserAgent(USER_AGENT);
|
||||||
|
client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore()));
|
||||||
|
client.post(getAbsoluteUrl(action), params, responseHandler);
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAbsoluteUrl(String action) {
|
private String getAbsoluteUrl(String action) {
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
package fr.gouv.etalab.mastodon.client;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream;
|
||||||
|
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Thomas on 21/05/2017.
|
||||||
|
* Patch for universal image loader to support TLS 1.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PatchBaseImageDownloader extends BaseImageDownloader {
|
||||||
|
|
||||||
|
private SSLSocketFactory sf;
|
||||||
|
|
||||||
|
public PatchBaseImageDownloader(Context context) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
|
initSSLSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSSLSocketFactory() {
|
||||||
|
sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||||
|
try {
|
||||||
|
sf = new MySSLSocketFactory();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
|
||||||
|
HttpURLConnection conn = createConnection(imageUri, extra);
|
||||||
|
if (conn instanceof HttpsURLConnection) {
|
||||||
|
((HttpsURLConnection) conn).setSSLSocketFactory(sf);
|
||||||
|
}
|
||||||
|
return new ContentLengthInputStream(new BufferedInputStream(conn.getInputStream(), BUFFER_SIZE), conn.getContentLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MySSLSocketFactory extends SSLSocketFactory {
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
|
||||||
|
MySSLSocketFactory() throws Exception {
|
||||||
|
super();
|
||||||
|
TrustManager tm = new X509TrustManager() {
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sslContext.init(null, new TrustManager[]{tm}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getDefaultCipherSuites() {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getSupportedCipherSuites() {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String host, int port) throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(host, port));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(host, port, localHost, localPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(host, port));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(address, port, localAddress, localPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(socket, host, port, autoClose));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket() throws IOException {
|
||||||
|
return enableTLSOnSocket(sslContext.getSocketFactory().createSocket());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Socket enableTLSOnSocket(Socket socket) {
|
||||||
|
if(socket != null && (socket instanceof SSLSocket)) {
|
||||||
|
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
|
||||||
|
}
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -171,19 +171,16 @@ public class NotificationsListAdapter extends BaseAdapter {
|
||||||
//Adds attachment -> disabled, to enable them uncomment the line below
|
//Adds attachment -> disabled, to enable them uncomment the line below
|
||||||
//loadAttachments(status, holder);
|
//loadAttachments(status, holder);
|
||||||
holder.notification_status_container.setVisibility(View.VISIBLE);
|
holder.notification_status_container.setVisibility(View.VISIBLE);
|
||||||
|
holder.notification_status_content.setOnClickListener(new View.OnClickListener() {
|
||||||
if( !status.getIn_reply_to_account_id().equals("null") || !status.getIn_reply_to_id().equals("null") ) {
|
@Override
|
||||||
holder.notification_status_content.setOnClickListener(new View.OnClickListener() {
|
public void onClick(View v) {
|
||||||
@Override
|
Intent intent = new Intent(context, ShowConversationActivity.class);
|
||||||
public void onClick(View v) {
|
Bundle b = new Bundle();
|
||||||
Intent intent = new Intent(context, ShowConversationActivity.class);
|
b.putString("statusId", status.getId());
|
||||||
Bundle b = new Bundle();
|
intent.putExtras(b);
|
||||||
b.putString("statusId", status.getId()); //Your id
|
context.startActivity(intent);
|
||||||
intent.putExtras(b); //Put your id to your next Intent
|
}
|
||||||
context.startActivity(intent);
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
switch (status.getVisibility()){
|
switch (status.getVisibility()){
|
||||||
case "public":
|
case "public":
|
||||||
holder.status_privacy.setImageResource(R.drawable.ic_action_globe);
|
holder.status_privacy.setImageResource(R.drawable.ic_action_globe);
|
||||||
|
@ -216,8 +213,8 @@ public class NotificationsListAdapter extends BaseAdapter {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(context, TootActivity.class);
|
Intent intent = new Intent(context, TootActivity.class);
|
||||||
Bundle b = new Bundle();
|
Bundle b = new Bundle();
|
||||||
b.putString("inReplyTo", notification.getStatus().getId()); //Your id
|
b.putString("inReplyTo", notification.getStatus().getId());
|
||||||
intent.putExtras(b); //Put your id to your next Intent
|
intent.putExtras(b);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -332,7 +332,10 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(context, ShowAccountActivity.class);
|
Intent intent = new Intent(context, ShowAccountActivity.class);
|
||||||
Bundle b = new Bundle();
|
Bundle b = new Bundle();
|
||||||
b.putString("accountId", status.getAccount().getId());
|
if( status.getReblog() == null)
|
||||||
|
b.putString("accountId", status.getAccount().getId());
|
||||||
|
else
|
||||||
|
b.putString("accountId", status.getReblog().getAccount().getId());
|
||||||
intent.putExtras(b);
|
intent.putExtras(b);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
@ -406,7 +409,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
|
||||||
String url = attachment.getPreview_url();
|
String url = attachment.getPreview_url();
|
||||||
if( url == null || url.trim().equals(""))
|
if( url == null || url.trim().equals(""))
|
||||||
url = attachment.getUrl();
|
url = attachment.getUrl();
|
||||||
if( url.trim().equals("https://mastodon.etalab.gouv.fr/files/small/missing.png"))
|
if( url.trim().contains("missing.png"))
|
||||||
continue;
|
continue;
|
||||||
imageLoader.displayImage(url, imageView, options);
|
imageLoader.displayImage(url, imageView, options);
|
||||||
imageView.setOnClickListener(new View.OnClickListener() {
|
imageView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
|
@ -14,10 +14,12 @@ package fr.gouv.etalab.mastodon.fragments;
|
||||||
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
|
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -58,6 +60,7 @@ public class DisplayAccountsFragment extends Fragment implements OnRetrieveAccou
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private int accountPerPage;
|
private int accountPerPage;
|
||||||
private String targetedId;
|
private String targetedId;
|
||||||
|
private boolean hideHeader = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
@ -69,6 +72,7 @@ public class DisplayAccountsFragment extends Fragment implements OnRetrieveAccou
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
type = (RetrieveAccountsAsyncTask.Type) bundle.get("type");
|
type = (RetrieveAccountsAsyncTask.Type) bundle.get("type");
|
||||||
targetedId = bundle.getString("targetedId", null);
|
targetedId = bundle.getString("targetedId", null);
|
||||||
|
hideHeader = bundle.getBoolean("hideHeader", false);
|
||||||
}
|
}
|
||||||
max_id = null;
|
max_id = null;
|
||||||
firstLoad = true;
|
firstLoad = true;
|
||||||
|
@ -78,7 +82,7 @@ public class DisplayAccountsFragment extends Fragment implements OnRetrieveAccou
|
||||||
swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeContainer);
|
swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeContainer);
|
||||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40);
|
accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40);
|
||||||
ListView lv_accounts = (ListView) rootView.findViewById(R.id.lv_accounts);
|
final ListView lv_accounts = (ListView) rootView.findViewById(R.id.lv_accounts);
|
||||||
|
|
||||||
mainLoader = (RelativeLayout) rootView.findViewById(R.id.loader);
|
mainLoader = (RelativeLayout) rootView.findViewById(R.id.loader);
|
||||||
nextElementLoader = (RelativeLayout) rootView.findViewById(R.id.loading_next_accounts);
|
nextElementLoader = (RelativeLayout) rootView.findViewById(R.id.loading_next_accounts);
|
||||||
|
@ -109,6 +113,35 @@ public class DisplayAccountsFragment extends Fragment implements OnRetrieveAccou
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Hide account header when scrolling for ShowAccountActivity
|
||||||
|
if( hideHeader ) {
|
||||||
|
lv_accounts.setOnScrollListener(new AbsListView.OnScrollListener() {
|
||||||
|
int lastFirstVisibleItem = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||||
|
if (view.getId() == lv_accounts.getId() && totalItemCount > visibleItemCount) {
|
||||||
|
final int currentFirstVisibleItem = lv_accounts.getFirstVisiblePosition();
|
||||||
|
|
||||||
|
if (currentFirstVisibleItem > lastFirstVisibleItem) {
|
||||||
|
Intent intent = new Intent(Helper.HEADER_ACCOUNT);
|
||||||
|
intent.putExtra("hide", true);
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
} else if (currentFirstVisibleItem < lastFirstVisibleItem) {
|
||||||
|
Intent intent = new Intent(Helper.HEADER_ACCOUNT);
|
||||||
|
intent.putExtra("hide", false);
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
lastFirstVisibleItem = currentFirstVisibleItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
|
@ -157,7 +190,6 @@ public class DisplayAccountsFragment extends Fragment implements OnRetrieveAccou
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRetrieveAccounts(List<Account> accounts) {
|
public void onRetrieveAccounts(List<Account> accounts) {
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,12 @@ package fr.gouv.etalab.mastodon.fragments;
|
||||||
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
|
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -25,7 +27,7 @@ import android.view.ViewGroup;
|
||||||
import android.widget.AbsListView;
|
import android.widget.AbsListView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -46,7 +48,6 @@ import fr.gouv.etalab.mastodon.interfaces.OnRetrieveFeedsInterface;
|
||||||
public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsInterface {
|
public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsInterface {
|
||||||
|
|
||||||
|
|
||||||
private TextView noAction;
|
|
||||||
private boolean flag_loading;
|
private boolean flag_loading;
|
||||||
private Context context;
|
private Context context;
|
||||||
private AsyncTask<Void, Void, Void> asyncTask;
|
private AsyncTask<Void, Void, Void> asyncTask;
|
||||||
|
@ -57,9 +58,9 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
|
||||||
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
|
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
|
||||||
private boolean firstLoad;
|
private boolean firstLoad;
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private int tootPerPage;
|
|
||||||
private String targetedId;
|
private String targetedId;
|
||||||
private String tag;
|
private String tag;
|
||||||
|
private boolean hideHeader = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
@ -72,6 +73,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
|
||||||
type = (RetrieveFeedsAsyncTask.Type) bundle.get("type");
|
type = (RetrieveFeedsAsyncTask.Type) bundle.get("type");
|
||||||
targetedId = bundle.getString("targetedId", null);
|
targetedId = bundle.getString("targetedId", null);
|
||||||
tag = bundle.getString("tag", null);
|
tag = bundle.getString("tag", null);
|
||||||
|
hideHeader = bundle.getBoolean("hideHeader", false);
|
||||||
}
|
}
|
||||||
max_id = null;
|
max_id = null;
|
||||||
flag_loading = true;
|
flag_loading = true;
|
||||||
|
@ -80,10 +82,9 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
|
||||||
boolean isOnWifi = Helper.isOnWIFI(context);
|
boolean isOnWifi = Helper.isOnWIFI(context);
|
||||||
swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeContainer);
|
swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeContainer);
|
||||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
tootPerPage = sharedpreferences.getInt(Helper.SET_TOOTS_PER_PAGE, 40);
|
|
||||||
int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS);
|
int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS);
|
||||||
|
|
||||||
ListView lv_status = (ListView) rootView.findViewById(R.id.lv_status);
|
final ListView lv_status = (ListView) rootView.findViewById(R.id.lv_status);
|
||||||
|
|
||||||
mainLoader = (RelativeLayout) rootView.findViewById(R.id.loader);
|
mainLoader = (RelativeLayout) rootView.findViewById(R.id.loader);
|
||||||
nextElementLoader = (RelativeLayout) rootView.findViewById(R.id.loading_next_status);
|
nextElementLoader = (RelativeLayout) rootView.findViewById(R.id.loading_next_status);
|
||||||
|
@ -117,6 +118,35 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Hide account header when scrolling for ShowAccountActivity
|
||||||
|
if( hideHeader ) {
|
||||||
|
lv_status.setOnScrollListener(new AbsListView.OnScrollListener() {
|
||||||
|
int lastFirstVisibleItem = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||||
|
if (view.getId() == lv_status.getId() && totalItemCount > visibleItemCount) {
|
||||||
|
final int currentFirstVisibleItem = lv_status.getFirstVisiblePosition();
|
||||||
|
|
||||||
|
if (currentFirstVisibleItem > lastFirstVisibleItem) {
|
||||||
|
Intent intent = new Intent(Helper.HEADER_ACCOUNT);
|
||||||
|
intent.putExtra("hide", true);
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
} else if (currentFirstVisibleItem < lastFirstVisibleItem) {
|
||||||
|
Intent intent = new Intent(Helper.HEADER_ACCOUNT);
|
||||||
|
intent.putExtra("hide", false);
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
lastFirstVisibleItem = currentFirstVisibleItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
|
@ -193,9 +223,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
|
||||||
}
|
}
|
||||||
swipeRefreshLayout.setRefreshing(false);
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
firstLoad = false;
|
firstLoad = false;
|
||||||
if( statuses != null && statuses.size() < tootPerPage )
|
flag_loading = false;
|
||||||
flag_loading = true;
|
|
||||||
else
|
|
||||||
flag_loading = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ public class SettingsNotificationsFragment extends Fragment {
|
||||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||||
boolean notif_validation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION, true);
|
boolean notif_validation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION, true);
|
||||||
boolean notif_wifi = sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false);
|
boolean notif_wifi = sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false);
|
||||||
|
boolean notif_silent = sharedpreferences.getBoolean(Helper.SET_NOTIF_SILENT, false);
|
||||||
|
boolean notif_hometimeline = sharedpreferences.getBoolean(Helper.SET_NOTIF_HOMETIMELINE, true);
|
||||||
|
|
||||||
final CheckBox set_notif_follow = (CheckBox) rootView.findViewById(R.id.set_notif_follow);
|
final CheckBox set_notif_follow = (CheckBox) rootView.findViewById(R.id.set_notif_follow);
|
||||||
final CheckBox set_notif_follow_add = (CheckBox) rootView.findViewById(R.id.set_notif_follow_add);
|
final CheckBox set_notif_follow_add = (CheckBox) rootView.findViewById(R.id.set_notif_follow_add);
|
||||||
|
@ -58,7 +60,9 @@ public class SettingsNotificationsFragment extends Fragment {
|
||||||
final CheckBox set_notif_follow_mention = (CheckBox) rootView.findViewById(R.id.set_notif_follow_mention);
|
final CheckBox set_notif_follow_mention = (CheckBox) rootView.findViewById(R.id.set_notif_follow_mention);
|
||||||
final CheckBox set_notif_follow_share = (CheckBox) rootView.findViewById(R.id.set_notif_follow_share);
|
final CheckBox set_notif_follow_share = (CheckBox) rootView.findViewById(R.id.set_notif_follow_share);
|
||||||
final CheckBox set_share_validation = (CheckBox) rootView.findViewById(R.id.set_share_validation);
|
final CheckBox set_share_validation = (CheckBox) rootView.findViewById(R.id.set_share_validation);
|
||||||
|
final CheckBox set_notif_hometimeline = (CheckBox) rootView.findViewById(R.id.set_notif_hometimeline);
|
||||||
final SwitchCompat switchCompatWIFI = (SwitchCompat) rootView.findViewById(R.id.set_wifi_only);
|
final SwitchCompat switchCompatWIFI = (SwitchCompat) rootView.findViewById(R.id.set_wifi_only);
|
||||||
|
final SwitchCompat switchCompatSilent = (SwitchCompat) rootView.findViewById(R.id.set_silence);
|
||||||
|
|
||||||
set_notif_follow.setChecked(notif_follow);
|
set_notif_follow.setChecked(notif_follow);
|
||||||
set_notif_follow_add.setChecked(notif_add);
|
set_notif_follow_add.setChecked(notif_add);
|
||||||
|
@ -66,9 +70,18 @@ public class SettingsNotificationsFragment extends Fragment {
|
||||||
set_notif_follow_mention.setChecked(notif_mention);
|
set_notif_follow_mention.setChecked(notif_mention);
|
||||||
set_notif_follow_share.setChecked(notif_share);
|
set_notif_follow_share.setChecked(notif_share);
|
||||||
set_share_validation.setChecked(notif_validation);
|
set_share_validation.setChecked(notif_validation);
|
||||||
|
set_notif_hometimeline.setChecked(notif_hometimeline);
|
||||||
switchCompatWIFI.setChecked(notif_wifi);
|
switchCompatWIFI.setChecked(notif_wifi);
|
||||||
|
switchCompatSilent.setChecked(notif_silent);
|
||||||
|
|
||||||
|
set_notif_hometimeline.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putBoolean(Helper.SET_NOTIF_HOMETIMELINE, set_notif_hometimeline.isChecked());
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
});
|
||||||
set_notif_follow.setOnClickListener(new View.OnClickListener() {
|
set_notif_follow.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -128,7 +141,15 @@ public class SettingsNotificationsFragment extends Fragment {
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
switchCompatSilent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
|
// Save the state here
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putBoolean(Helper.SET_NOTIF_SILENT, isChecked);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
});
|
||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,17 +19,25 @@ package fr.gouv.etalab.mastodon.helper;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.DownloadManager;
|
import android.app.DownloadManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.media.RingtoneManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v4.app.NotificationManagerCompat;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.loopj.android.http.BuildConfig;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
@ -38,9 +46,12 @@ import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import fr.gouv.etalab.mastodon.activities.MainActivity;
|
||||||
import mastodon.etalab.gouv.fr.mastodon.R;
|
import mastodon.etalab.gouv.fr.mastodon.R;
|
||||||
import fr.gouv.etalab.mastodon.client.API;
|
import fr.gouv.etalab.mastodon.client.API;
|
||||||
|
|
||||||
|
import static android.app.Notification.DEFAULT_SOUND;
|
||||||
|
import static android.app.Notification.DEFAULT_VIBRATE;
|
||||||
import static android.content.Context.DOWNLOAD_SERVICE;
|
import static android.content.Context.DOWNLOAD_SERVICE;
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,10 +88,10 @@ public class Helper {
|
||||||
public static final String SCOPES = "scopes";
|
public static final String SCOPES = "scopes";
|
||||||
public static final String WEBSITE = "website";
|
public static final String WEBSITE = "website";
|
||||||
public static final String LAST_NOTIFICATION_MAX_ID = "last_notification_max_id";
|
public static final String LAST_NOTIFICATION_MAX_ID = "last_notification_max_id";
|
||||||
|
public static final String LAST_HOMETIMELINE_MAX_ID = "last_hometimeline_max_id";
|
||||||
//Notifications
|
//Notifications
|
||||||
public static final String NOTIFICATION_TYPE = "notification_type";
|
|
||||||
public static final int NOTIFICATION_INTENT = 1;
|
public static final int NOTIFICATION_INTENT = 1;
|
||||||
|
public static final int HOME_TIMELINE_INTENT = 2;
|
||||||
|
|
||||||
//Settings
|
//Settings
|
||||||
public static final String SET_TOOTS_PER_PAGE = "set_toots_per_page";
|
public static final String SET_TOOTS_PER_PAGE = "set_toots_per_page";
|
||||||
|
@ -99,6 +110,7 @@ public class Helper {
|
||||||
public static final String SET_NOTIF_SHARE = "set_notif_follow_share";
|
public static final String SET_NOTIF_SHARE = "set_notif_follow_share";
|
||||||
public static final String SET_NOTIF_VALIDATION = "set_share_validation";
|
public static final String SET_NOTIF_VALIDATION = "set_share_validation";
|
||||||
public static final String SET_WIFI_ONLY = "set_wifi_only";
|
public static final String SET_WIFI_ONLY = "set_wifi_only";
|
||||||
|
public static final String SET_NOTIF_HOMETIMELINE = "set_notif_hometimeline";
|
||||||
public static final String SET_NOTIF_SILENT = "set_notif_silent";
|
public static final String SET_NOTIF_SILENT = "set_notif_silent";
|
||||||
|
|
||||||
//End points
|
//End points
|
||||||
|
@ -107,16 +119,17 @@ public class Helper {
|
||||||
|
|
||||||
//Refresh job
|
//Refresh job
|
||||||
public static final int MINUTES_BETWEEN_NOTIFICATIONS_REFRESH = 15;
|
public static final int MINUTES_BETWEEN_NOTIFICATIONS_REFRESH = 15;
|
||||||
|
public static final int MINUTES_BETWEEN_HOME_TIMELINE = 30;
|
||||||
|
|
||||||
//Intent
|
//Intent
|
||||||
public static final String INTENT_ACTION = "intent_action";
|
public static final String INTENT_ACTION = "intent_action";
|
||||||
public static final int INTENT_NOTIFICATION = 1;
|
|
||||||
|
|
||||||
//Receiver
|
//Receiver
|
||||||
public static final String SEARCH_VALIDATE_ACCOUNT = "search_validate_account";
|
public static final String SEARCH_VALIDATE_ACCOUNT = "search_validate_account";
|
||||||
|
public static final String HEADER_ACCOUNT = "header_account";
|
||||||
|
|
||||||
|
//User agent
|
||||||
|
public static final String USER_AGENT = "Mastalab/"+ BuildConfig.VERSION_NAME + " Android/"+ Build.VERSION.RELEASE;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Check if the user is connected to Internet
|
* Check if the user is connected to Internet
|
||||||
|
@ -364,4 +377,40 @@ public class Helper {
|
||||||
alert.show();
|
alert.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends notification with intent
|
||||||
|
* @param context Context
|
||||||
|
* @param intentAction int intent action
|
||||||
|
* @param notificationId int id of the notification
|
||||||
|
* @param icon Bitmap profile picture
|
||||||
|
* @param title String title of the notification
|
||||||
|
* @param message String message for the notification
|
||||||
|
*/
|
||||||
|
public static void notify_user(Context context, int intentAction, int notificationId, Bitmap icon, String title, String message ) {
|
||||||
|
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
// prepare intent which is triggered if the user click on the notification
|
||||||
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||||
|
final Intent intent = new Intent(context, MainActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );
|
||||||
|
intent.putExtra(INTENT_ACTION, intentAction);
|
||||||
|
PendingIntent pIntent = PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||||
|
// build notification
|
||||||
|
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
|
||||||
|
.setSmallIcon(R.drawable.notification_icon)
|
||||||
|
.setTicker(message)
|
||||||
|
.setWhen(System.currentTimeMillis())
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setContentIntent(pIntent)
|
||||||
|
.setContentText(message);
|
||||||
|
if( sharedpreferences.getBoolean(Helper.SET_NOTIF_SILENT,false) ) {
|
||||||
|
notificationBuilder.setDefaults(DEFAULT_VIBRATE);
|
||||||
|
}else {
|
||||||
|
notificationBuilder.setDefaults(DEFAULT_SOUND);
|
||||||
|
}
|
||||||
|
notificationBuilder.setContentTitle(title);
|
||||||
|
notificationBuilder.setLargeIcon(icon);
|
||||||
|
notificationManager.notify(notificationId, notificationBuilder.build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* Copyright 2017 Thomas Schneider
|
||||||
|
*
|
||||||
|
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Mastodon Etalab 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 Thomas Schneider; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
package fr.gouv.etalab.mastodon.interfaces;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import fr.gouv.etalab.mastodon.client.Entities.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Thomas on 20/05/2017.
|
||||||
|
* Interface when home timeline toots have been retrieved
|
||||||
|
*/
|
||||||
|
public interface OnRetrieveHomeTimelineServiceInterface {
|
||||||
|
void onRetrieveHomeTimelineService(List<Status> statuses, String acct);
|
||||||
|
}
|
|
@ -28,6 +28,8 @@ public class ApplicationJob implements JobCreator {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case NotificationsSyncJob.NOTIFICATION_REFRESH:
|
case NotificationsSyncJob.NOTIFICATION_REFRESH:
|
||||||
return new NotificationsSyncJob();
|
return new NotificationsSyncJob();
|
||||||
|
case HomeTimelineSyncJob.HOME_TIMELINE:
|
||||||
|
return new HomeTimelineSyncJob();
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
package fr.gouv.etalab.mastodon.jobs;
|
||||||
|
/* Copyright 2017 Thomas Schneider
|
||||||
|
*
|
||||||
|
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Mastodon Etalab 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 Thomas Schneider; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.evernote.android.job.Job;
|
||||||
|
import com.evernote.android.job.JobManager;
|
||||||
|
import com.evernote.android.job.JobRequest;
|
||||||
|
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
|
||||||
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import fr.gouv.etalab.mastodon.asynctasks.RetrieveHomeTimelineServiceAsyncTask;
|
||||||
|
import fr.gouv.etalab.mastodon.client.Entities.Account;
|
||||||
|
import fr.gouv.etalab.mastodon.client.Entities.Status;
|
||||||
|
import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader;
|
||||||
|
import fr.gouv.etalab.mastodon.helper.Helper;
|
||||||
|
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveHomeTimelineServiceInterface;
|
||||||
|
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
|
||||||
|
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
|
||||||
|
import mastodon.etalab.gouv.fr.mastodon.R;
|
||||||
|
|
||||||
|
import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT;
|
||||||
|
import static fr.gouv.etalab.mastodon.helper.Helper.notify_user;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Thomas on 20/05/2017.
|
||||||
|
* Notifications for home timeline job
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineServiceInterface{
|
||||||
|
|
||||||
|
static final String HOME_TIMELINE = "home_timeline";
|
||||||
|
private int notificationId;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Result onRunJob(Params params) {
|
||||||
|
callAsynchronousTask();
|
||||||
|
return Result.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static int schedule(boolean updateCurrent){
|
||||||
|
|
||||||
|
Set<JobRequest> jobRequests = JobManager.instance().getAllJobRequestsForTag(HOME_TIMELINE);
|
||||||
|
if (!jobRequests.isEmpty() && !updateCurrent) {
|
||||||
|
return jobRequests.iterator().next().getJobId();
|
||||||
|
}
|
||||||
|
return new JobRequest.Builder(HomeTimelineSyncJob.HOME_TIMELINE)
|
||||||
|
.setPeriodic(TimeUnit.MINUTES.toMillis(Helper.MINUTES_BETWEEN_HOME_TIMELINE), TimeUnit.MINUTES.toMillis(5))
|
||||||
|
.setPersisted(true)
|
||||||
|
.setUpdateCurrent(updateCurrent)
|
||||||
|
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
|
||||||
|
.setRequirementsEnforced(false)
|
||||||
|
.build()
|
||||||
|
.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task in background starts here.
|
||||||
|
*/
|
||||||
|
private void callAsynchronousTask() {
|
||||||
|
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
boolean notif_hometimeline = sharedpreferences.getBoolean(Helper.SET_NOTIF_HOMETIMELINE, true);
|
||||||
|
//User disagree with home timeline refresh
|
||||||
|
if( !notif_hometimeline)
|
||||||
|
return; //Nothing is done
|
||||||
|
SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
//If an Internet connection and user agrees with notification refresh
|
||||||
|
//If WIFI only and on WIFI OR user defined any connections to use the service.
|
||||||
|
if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getContext())) {
|
||||||
|
List<Account> accounts = new AccountDAO(getContext(),db).getAllAccount();
|
||||||
|
//It means there is no user in DB.
|
||||||
|
if( accounts == null )
|
||||||
|
return;
|
||||||
|
//Retrieve users in db that owner has.
|
||||||
|
for (Account account: accounts) {
|
||||||
|
String since_id = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + account.getAcct(), null);
|
||||||
|
notificationId = (int) Math.round(Double.parseDouble(account.getId())/1000);
|
||||||
|
new RetrieveHomeTimelineServiceAsyncTask(getContext(), since_id, account.getAcct(), HomeTimelineSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRetrieveHomeTimelineService(List<Status> statuses, String acct) {
|
||||||
|
if( statuses == null || statuses.size() == 0)
|
||||||
|
return;
|
||||||
|
Bitmap icon_notification = null;
|
||||||
|
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
String max_id = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + acct, null);
|
||||||
|
//No previous notifications in cache, so no notification will be sent
|
||||||
|
if( max_id != null ){
|
||||||
|
String message;
|
||||||
|
String title = null;
|
||||||
|
for(Status status: statuses){
|
||||||
|
//The notification associated to max_id is discarded as it is supposed to have already been sent
|
||||||
|
if( status.getId().equals(max_id))
|
||||||
|
continue;
|
||||||
|
String notificationUrl = status.getAccount().getAvatar();
|
||||||
|
if( notificationUrl != null && icon_notification == null){
|
||||||
|
try {
|
||||||
|
ImageLoader imageLoaderNoty = ImageLoader.getInstance();
|
||||||
|
File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name));
|
||||||
|
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getContext())
|
||||||
|
.imageDownloader(new PatchBaseImageDownloader(getContext()))
|
||||||
|
.threadPoolSize(5)
|
||||||
|
.threadPriority(Thread.MIN_PRIORITY + 3)
|
||||||
|
.denyCacheImageMultipleSizesInMemory()
|
||||||
|
.diskCache(new UnlimitedDiskCache(cacheDir))
|
||||||
|
.build();
|
||||||
|
imageLoaderNoty.init(config);
|
||||||
|
icon_notification = imageLoaderNoty.loadImageSync(notificationUrl);
|
||||||
|
title = getContext().getResources().getString(R.string.notif_pouet, status.getAccount().getDisplay_name());
|
||||||
|
}catch (Exception e){
|
||||||
|
icon_notification = BitmapFactory.decodeResource(getContext().getResources(),
|
||||||
|
R.drawable.mastodonlogo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(statuses.size() > 0 )
|
||||||
|
message = getContext().getResources().getQuantityString(R.plurals.other_notif_hometimeline, statuses.size(), statuses.size());
|
||||||
|
else
|
||||||
|
message = "";
|
||||||
|
notify_user(getContext(), HOME_TIMELINE_INTENT, notificationId, icon_notification,title,message);
|
||||||
|
}
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + acct, statuses.get(0).getId());
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -13,18 +13,15 @@ package fr.gouv.etalab.mastodon.jobs;
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
|
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.media.RingtoneManager;
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.support.v4.app.NotificationManagerCompat;
|
|
||||||
|
|
||||||
import com.evernote.android.job.Job;
|
import com.evernote.android.job.Job;
|
||||||
import com.evernote.android.job.JobManager;
|
import com.evernote.android.job.JobManager;
|
||||||
|
@ -39,7 +36,7 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import fr.gouv.etalab.mastodon.activities.MainActivity;
|
import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader;
|
||||||
import fr.gouv.etalab.mastodon.helper.Helper;
|
import fr.gouv.etalab.mastodon.helper.Helper;
|
||||||
import mastodon.etalab.gouv.fr.mastodon.R;
|
import mastodon.etalab.gouv.fr.mastodon.R;
|
||||||
import fr.gouv.etalab.mastodon.asynctasks.RetrieveNotificationsAsyncTask;
|
import fr.gouv.etalab.mastodon.asynctasks.RetrieveNotificationsAsyncTask;
|
||||||
|
@ -49,8 +46,8 @@ import fr.gouv.etalab.mastodon.interfaces.OnRetrieveNotificationsInterface;
|
||||||
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
|
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
|
||||||
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
|
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
|
||||||
|
|
||||||
import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION;
|
import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT;
|
||||||
import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_NOTIFICATION;
|
import static fr.gouv.etalab.mastodon.helper.Helper.notify_user;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,8 +57,7 @@ import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_NOTIFICATION;
|
||||||
|
|
||||||
public class NotificationsSyncJob extends Job implements OnRetrieveNotificationsInterface{
|
public class NotificationsSyncJob extends Job implements OnRetrieveNotificationsInterface{
|
||||||
|
|
||||||
public static final String NOTIFICATION_REFRESH = "job_notification";
|
static final String NOTIFICATION_REFRESH = "job_notification";
|
||||||
private int jobId;
|
|
||||||
private int notificationId;
|
private int notificationId;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -73,7 +69,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static int schedule(Context context, boolean updateCurrent){
|
public static int schedule(boolean updateCurrent){
|
||||||
|
|
||||||
Set<JobRequest> jobRequests = JobManager.instance().getAllJobRequestsForTag(NOTIFICATION_REFRESH);
|
Set<JobRequest> jobRequests = JobManager.instance().getAllJobRequestsForTag(NOTIFICATION_REFRESH);
|
||||||
if (!jobRequests.isEmpty() && !updateCurrent) {
|
if (!jobRequests.isEmpty() && !updateCurrent) {
|
||||||
|
@ -99,6 +95,15 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
//If an Internet connection and user agrees with notification refresh
|
//If an Internet connection and user agrees with notification refresh
|
||||||
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
//Check which notifications the user wants to see
|
||||||
|
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||||
|
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||||
|
boolean notif_ask = sharedpreferences.getBoolean(Helper.SET_NOTIF_ASK, true);
|
||||||
|
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||||
|
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||||
|
//User disagree with all notifications
|
||||||
|
if( !notif_follow && !notif_add && !notif_ask && !notif_mention && !notif_share)
|
||||||
|
return; //Nothing is done
|
||||||
//If WIFI only and on WIFI OR user defined any connections to use the service.
|
//If WIFI only and on WIFI OR user defined any connections to use the service.
|
||||||
if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getContext())) {
|
if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getContext())) {
|
||||||
List<Account> accounts = new AccountDAO(getContext(),db).getAllAccount();
|
List<Account> accounts = new AccountDAO(getContext(),db).getAllAccount();
|
||||||
|
@ -124,7 +129,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||||
boolean notif_ask = sharedpreferences.getBoolean(Helper.SET_NOTIF_ASK, true);
|
//boolean notif_ask = sharedpreferences.getBoolean(Helper.SET_NOTIF_ASK, true);
|
||||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||||
String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + acct, null);
|
String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + acct, null);
|
||||||
|
@ -137,7 +142,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
int newShare = 0;
|
int newShare = 0;
|
||||||
String notificationUrl = null;
|
String notificationUrl = null;
|
||||||
String title = null;
|
String title = null;
|
||||||
String message = null;
|
String message;
|
||||||
for(Notification notification: notifications){
|
for(Notification notification: notifications){
|
||||||
//The notification associated to max_id is discarded as it is supposed to have already been sent
|
//The notification associated to max_id is discarded as it is supposed to have already been sent
|
||||||
if( notification.getId().equals(max_id))
|
if( notification.getId().equals(max_id))
|
||||||
|
@ -186,6 +191,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
ImageLoader imageLoaderNoty = ImageLoader.getInstance();
|
ImageLoader imageLoaderNoty = ImageLoader.getInstance();
|
||||||
File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name));
|
File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name));
|
||||||
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getContext())
|
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getContext())
|
||||||
|
.imageDownloader(new PatchBaseImageDownloader(getContext()))
|
||||||
.threadPoolSize(5)
|
.threadPoolSize(5)
|
||||||
.threadPriority(Thread.MIN_PRIORITY + 3)
|
.threadPriority(Thread.MIN_PRIORITY + 3)
|
||||||
.denyCacheImageMultipleSizesInMemory()
|
.denyCacheImageMultipleSizesInMemory()
|
||||||
|
@ -195,7 +201,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
icon_notification = imageLoaderNoty.loadImageSync(notificationUrl);
|
icon_notification = imageLoaderNoty.loadImageSync(notificationUrl);
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
icon_notification = BitmapFactory.decodeResource(getContext().getResources(),
|
icon_notification = BitmapFactory.decodeResource(getContext().getResources(),
|
||||||
R.drawable.mastodon_logo);
|
R.drawable.mastodonlogo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,7 +213,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
message = getContext().getResources().getQuantityString(R.plurals.other_notifications, other, other);
|
message = getContext().getResources().getQuantityString(R.plurals.other_notifications, other, other);
|
||||||
else
|
else
|
||||||
message = "";
|
message = "";
|
||||||
notify_user(icon_notification,title,message);
|
notify_user(getContext(), NOTIFICATION_INTENT, notificationId, icon_notification,title,message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
@ -216,36 +222,4 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends notification with intent
|
|
||||||
* @param icon Bitmap profile picture
|
|
||||||
* @param title String title of the notification
|
|
||||||
* @param message String message for the notification
|
|
||||||
*/
|
|
||||||
private void notify_user(Bitmap icon, String title, String message ) {
|
|
||||||
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
|
||||||
// prepare intent which is triggered if the user click on the notification
|
|
||||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getContext());
|
|
||||||
final Intent intent = new Intent(getContext(), MainActivity.class);
|
|
||||||
intent.putExtra(Helper.NOTIFICATION_TYPE, Helper.NOTIFICATION_INTENT);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );
|
|
||||||
intent.putExtra(INTENT_ACTION, INTENT_NOTIFICATION);
|
|
||||||
PendingIntent pIntent = PendingIntent.getActivity(getContext(), notificationId, intent, PendingIntent.FLAG_ONE_SHOT);
|
|
||||||
|
|
||||||
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
|
||||||
// build notification
|
|
||||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getContext())
|
|
||||||
.setSmallIcon(R.drawable.notification_icon)
|
|
||||||
.setTicker(message)
|
|
||||||
.setWhen(System.currentTimeMillis())
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setContentIntent(pIntent)
|
|
||||||
.setContentText(message);
|
|
||||||
if( !sharedpreferences.getBoolean(Helper.SET_NOTIF_SILENT,false) ) {
|
|
||||||
notificationBuilder.setDefaults(-1);
|
|
||||||
}
|
|
||||||
notificationBuilder.setContentTitle(title);
|
|
||||||
notificationBuilder.setLargeIcon(icon);
|
|
||||||
notificationManager.notify(notificationId, notificationBuilder.build());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,6 @@
|
||||||
<item>
|
<item>
|
||||||
<bitmap
|
<bitmap
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:src="@drawable/mastodon_logo"/>
|
android:src="@drawable/mastodonlogo"/>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
Binary file not shown.
Before Width: | Height: | Size: 59 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
|
@ -20,6 +20,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
|
<!-- About app name -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_marginTop="20dp"
|
android:layout_marginTop="20dp"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
<!-- About version -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:id="@+id/about_version"
|
android:id="@+id/about_version"
|
||||||
|
@ -36,29 +38,75 @@
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
<TextView
|
|
||||||
android:id="@+id/about_developer"
|
<!-- About developer -->
|
||||||
android:layout_marginTop="10dp"
|
<LinearLayout
|
||||||
android:textSize="16sp"
|
android:orientation="horizontal"
|
||||||
android:layout_gravity="center_horizontal"
|
android:padding="10dp"
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:clickable="true"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/about_license"
|
android:textSize="16sp"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_gravity="center"
|
||||||
android:textSize="16sp"
|
android:gravity="center"
|
||||||
android:layout_gravity="center_horizontal"
|
android:clickable="true"
|
||||||
android:gravity="center_horizontal"
|
android:layout_weight="3"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:text="@string/about_developer"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/about_developer"
|
||||||
|
android:text="@string/about_developer_action"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
style="@style/Base.Widget.AppCompat.Button.Colored"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- About license -->
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="10dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/about_code"
|
android:text="@string/about_license"
|
||||||
android:layout_marginTop="10dp"
|
android:textSize="16sp"
|
||||||
android:textSize="16sp"
|
android:layout_gravity="center"
|
||||||
android:layout_gravity="center_horizontal"
|
android:gravity="center"
|
||||||
android:gravity="center_horizontal"
|
android:layout_weight="3"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/about_license"
|
||||||
|
android:text="@string/about_license_action"
|
||||||
|
style="@style/Base.Widget.AppCompat.Button.Colored"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- About license -->
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="10dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/about_code"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/about_code"
|
||||||
|
android:text="@string/about_code_action"
|
||||||
|
style="@style/Base.Widget.AppCompat.Button.Colored"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -24,25 +24,41 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_width="100dp"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:padding="20dp"
|
android:src="@drawable/mastodonlogo"
|
||||||
android:layout_weight="2"
|
android:layout_height="100dp"
|
||||||
android:src="@drawable/mastodon_logo"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_margin="50dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/login_uid"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:hint="@string/email"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id = "@+id/login_passwd"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:hint="@string/password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/login_button"
|
android:id="@+id/login_button"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:drawableLeft="@drawable/mastodon_icon"
|
android:drawableLeft="@drawable/mastodon_icon"
|
||||||
android:drawableStart="@drawable/mastodon_icon"
|
android:drawableStart="@drawable/mastodon_icon"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
android:drawablePadding="10dp"
|
android:drawablePadding="10dp"
|
||||||
android:paddingLeft="15dp"
|
android:paddingLeft="15dp"
|
||||||
android:paddingStart="15dp"
|
android:paddingStart="15dp"
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -99,6 +100,15 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
<TextView
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
android:id="@+id/account_note"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:autoLink="web"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
<android.support.design.widget.AppBarLayout
|
<android.support.design.widget.AppBarLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
|
|
@ -92,7 +92,6 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/mastodon_logo"
|
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -109,7 +108,6 @@
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:src="@drawable/mastodon_logo"
|
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
@ -123,7 +121,6 @@
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/mastodon_logo"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
<ImageView
|
<ImageView
|
||||||
|
@ -133,7 +130,6 @@
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/mastodon_logo"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -69,7 +69,20 @@
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
<!-- END NOTIFICATIONS SETTINGS -->
|
<!-- END NOTIFICATIONS SETTINGS -->
|
||||||
|
|
||||||
|
<!-- NOTIFICATION CONTENT NEW -->
|
||||||
|
<TextView
|
||||||
|
android:text="@string/set_title_news"
|
||||||
|
android:background="@drawable/shape_border_bottom_settings"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<!-- New hometimeline content -->
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/set_notif_hometimeline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:text="@string/set_notification_news"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
<!-- MORE OPTIONS SETTINGS -->
|
<!-- MORE OPTIONS SETTINGS -->
|
||||||
<TextView
|
<TextView
|
||||||
android:text="@string/settings_title_more_options"
|
android:text="@string/settings_title_more_options"
|
||||||
|
@ -105,7 +118,25 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<TextView
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/set_notif_silent"/>
|
||||||
|
<android.support.v7.widget.SwitchCompat
|
||||||
|
android:id="@+id/set_silence"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
<string name="cancel">Annuler</string>
|
<string name="cancel">Annuler</string>
|
||||||
<string name="download">Télécharger</string>
|
<string name="download">Télécharger</string>
|
||||||
<string name="download_file">Télécharger %1$s</string>
|
<string name="download_file">Télécharger %1$s</string>
|
||||||
|
<string name="password">Mot de passe</string>
|
||||||
|
<string name="email">Email</string>
|
||||||
<!--- Menu -->
|
<!--- Menu -->
|
||||||
<string name="home_menu">Accueil</string>
|
<string name="home_menu">Accueil</string>
|
||||||
<string name="home_timeline">Accueil</string>
|
<string name="home_timeline">Accueil</string>
|
||||||
|
@ -119,15 +121,20 @@
|
||||||
|
|
||||||
<!-- About -->
|
<!-- About -->
|
||||||
<string name="about_vesrion">Version %1$s</string>
|
<string name="about_vesrion">Version %1$s</string>
|
||||||
<string name="about_developer">Développeur : <a href="https://mastodon.etalab.gouv.fr/@tschneider">@tschneider@mastodon.etalab.gouv.fr</a></string>
|
<string name="about_developer">Développeur :</string>
|
||||||
<string name="about_license">Licence : <a href="https://www.gnu.org/licenses/quick-guide-gplv3.fr.html">GNU GPL V3</a></string>
|
<string name="about_developer_action">\@tschneider</string>
|
||||||
<string name="about_code">Code source : <a href="https://bitbucket.org/tom79/mastodon_etalab">bitbucket</a></string>
|
<string name="about_license">Licence : </string>
|
||||||
|
<string name="about_license_action">GNU GPL V3</string>
|
||||||
|
<string name="about_code">Code source : </string>
|
||||||
|
<string name="about_code_action">bitbucket</string>
|
||||||
<!-- Conversation -->
|
<!-- Conversation -->
|
||||||
<string name="conversation">Conversation</string>
|
<string name="conversation">Conversation</string>
|
||||||
|
|
||||||
<!-- Accounts -->
|
<!-- Accounts -->
|
||||||
<string name="no_accounts">Aucun compte à afficher</string>
|
<string name="no_accounts">Aucun compte à afficher</string>
|
||||||
|
<string name="status_cnt">Pouets \n %d</string>
|
||||||
|
<string name="following_cnt">Abonnements \n %d</string>
|
||||||
|
<string name="followers_cnt">Abonnés \n %d</string>
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
<string name="no_notifications">Aucune notification à afficher</string>
|
<string name="no_notifications">Aucune notification à afficher</string>
|
||||||
|
@ -135,10 +142,15 @@
|
||||||
<string name="notif_reblog">a partagé votre pouet</string>
|
<string name="notif_reblog">a partagé votre pouet</string>
|
||||||
<string name="notif_favourite">a ajouté votre pouet à ses favoris</string>
|
<string name="notif_favourite">a ajouté votre pouet à ses favoris</string>
|
||||||
<string name="notif_follow">vous a suivi</string>
|
<string name="notif_follow">vous a suivi</string>
|
||||||
|
<string name="notif_pouet">Nouveau pouet de %1$s</string>
|
||||||
<plurals name="other_notifications">
|
<plurals name="other_notifications">
|
||||||
<item quantity="one">et %d autre notification</item>
|
<item quantity="one">et %d autre notification</item>
|
||||||
<item quantity="other">et %d autres notifications</item>
|
<item quantity="other">et %d autres notifications</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<plurals name="other_notif_hometimeline">
|
||||||
|
<item quantity="one">et un autre pouet à découvrir</item>
|
||||||
|
<item quantity="other">et %d autres pouets à découvrir</item>
|
||||||
|
</plurals>
|
||||||
<!-- HEADER -->
|
<!-- HEADER -->
|
||||||
<string name="status">Pouets</string>
|
<string name="status">Pouets</string>
|
||||||
<string name="following">Abonnements</string>
|
<string name="following">Abonnements</string>
|
||||||
|
@ -160,6 +172,7 @@
|
||||||
<string name="toast_unstatus">Le pouet a été supprimé !</string>
|
<string name="toast_unstatus">Le pouet a été supprimé !</string>
|
||||||
<string name="toast_error">Oups ! Une erreur s\'est produite !</string>
|
<string name="toast_error">Oups ! Une erreur s\'est produite !</string>
|
||||||
<string name="toast_error_loading_account">Une erreur s\'est produite en chargeant le compte !</string>
|
<string name="toast_error_loading_account">Une erreur s\'est produite en chargeant le compte !</string>
|
||||||
|
<string name="toast_error_login">Impossible de vous connecter !</string>
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<string name="settings_title_optimisation">Optimisation du chargement</string>
|
<string name="settings_title_optimisation">Optimisation du chargement</string>
|
||||||
<string name="set_toots_page">Nombre de pouets par chargement</string>
|
<string name="set_toots_page">Nombre de pouets par chargement</string>
|
||||||
|
@ -183,6 +196,8 @@
|
||||||
<string name="set_wifi_only">Notifier en WIFI seulement</string>
|
<string name="set_wifi_only">Notifier en WIFI seulement</string>
|
||||||
<string name="set_notif_silent">Utiliser le vibreur</string>
|
<string name="set_notif_silent">Utiliser le vibreur</string>
|
||||||
|
|
||||||
|
<string name="set_title_news">Actualités</string>
|
||||||
|
<string name="set_notification_news">Notifier lors de nouveaux pouets sur la page d\'accueil</string>
|
||||||
|
|
||||||
<string name="action_follow">Suivre</string>
|
<string name="action_follow">Suivre</string>
|
||||||
<string name="action_unfollow">Se désabonner</string>
|
<string name="action_unfollow">Se désabonner</string>
|
||||||
|
|
|
@ -5,7 +5,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.3.1'
|
classpath 'com.android.tools.build:gradle:2.3.2'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
|
Loading…
Reference in New Issue