Update app icon, use AppAuth library for Oauth (instead of Play Services), fix vibrate/silent feature on pre-6.0

This commit is contained in:
Trevor Slocum 2017-10-08 13:44:14 -07:00
parent 5b73d71e86
commit 840a5fd039
39 changed files with 1235 additions and 529 deletions

View File

@ -1,3 +1,9 @@
1.4.1:
- Updated app icon (thanks Symbolisch)
- Changed MediNET Oauth method from Play Services to open-source library AppAuth
- Fix set to vibrate/silent feature on pre-Android 6.0 devices
- App now requires Android 4.1+ (was 4.0+)
1.4.0:
- Fix alarm icon appearing in the status bar when Daily Reminder is enabled
- Increase fidelity of the default gong sound

View File

@ -1,31 +1,37 @@
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.1'
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
apply plugin: 'com.android.application'
repositories {
mavenCentral()
maven {
url "https://maven.google.com"
}
jcenter()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
android {
compileSdkVersion 24
buildToolsVersion '24.0.2'
compileSdkVersion 26
buildToolsVersion '26.0.2'
defaultConfig {
minSdkVersion 14
targetSdkVersion 24
minSdkVersion 16
targetSdkVersion 26
versionCode 140
versionName "1.4.0"
versionCode 141
versionName "1.4.1"
jackOptions {
enabled true
}
applicationId "sh.ftp.rocketninelabs.meditationassistant"
manifestPlaceholders = [
'appAuthRedirectScheme': applicationId
]
}
lintOptions {
@ -33,6 +39,12 @@ android {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
@ -58,41 +70,56 @@ android {
productFlavors {
free {
applicationId "sh.ftp.rocketninelabs.meditationassistant"
buildConfigField "String", "GOOGLEOAUTHKEY", "\"163346957857.apps.googleusercontent.com\""
}
full {
buildConfigField "String", "GOOGLEOAUTHKEY", "\"153054147563.apps.googleusercontent.com\""
applicationId "sh.ftp.rocketninelabs.meditationassistant.full"
manifestPlaceholders = [
'appAuthRedirectScheme': applicationId
]
}
opensource {
applicationId "sh.ftp.rocketninelabs.meditationassistant.opensource"
buildConfigField "String", "GOOGLEOAUTHKEY", "\"821225341172-64l3l6kdk9ull9lc7fjgeuuu7nee7pb9.apps.googleusercontent.com\""
delete('google-services.json')
applicationId "sh.ftp.rocketninelabs.meditationassistant.opensource"
manifestPlaceholders = [
'appAuthRedirectScheme': applicationId
]
}
}
}
dependencies {
compile 'com.android.support:support-v4:24.2.1'
freeCompile 'com.google.android.gms:play-services-base:9.6.1'
fullCompile 'com.google.android.gms:play-services-base:9.6.1'
freeCompile 'com.google.android.gms:play-services-identity:9.6.1'
fullCompile 'com.google.android.gms:play-services-identity:9.6.1'
freeCompile 'com.google.android.gms:play-services-analytics:9.6.1'
fullCompile 'com.google.android.gms:play-services-analytics:9.6.1'
freeCompile 'com.google.android.gms:play-services-wearable:9.6.1'
fullCompile 'com.google.android.gms:play-services-wearable:9.6.1'
freeCompile 'com.google.android.gms:play-services-appinvite:9.6.1'
fullCompile 'com.google.android.gms:play-services-appinvite:9.6.1'
freeCompile 'com.google.android.gms:play-services-fitness:9.6.1'
fullCompile 'com.google.android.gms:play-services-fitness:9.6.1'
freeCompile 'com.google.android.gms:play-services-ads:9.6.1'
compile "com.android.support:support-compat:26.1.0"
compile "com.android.support:support-core-utils:26.1.0"
compile "com.android.support:support-core-ui:26.1.0"
compile "com.android.support:support-media-compat:26.1.0"
compile "com.android.support:support-fragment:26.1.0"
compile "com.android.support:recyclerview-v7:26.1.0"
compile "com.android.support:design:26.1.0"
compile "net.openid:appauth:0.7.0"
compile "com.squareup.okio:okio:1.13.0"
freeCompile 'com.google.android.gms:play-services-base:11.4.2'
fullCompile 'com.google.android.gms:play-services-base:11.4.2'
freeCompile 'com.google.android.gms:play-services-analytics:11.4.2'
fullCompile 'com.google.android.gms:play-services-analytics:11.4.2'
//freeCompile 'com.google.android.gms:play-services-wearable:11.4.2'
//fullCompile 'com.google.android.gms:play-services-wearable:11.4.2'
freeCompile 'com.google.android.gms:play-services-appinvite:11.4.2'
fullCompile 'com.google.android.gms:play-services-appinvite:11.4.2'
freeCompile 'com.google.android.gms:play-services-fitness:11.4.2'
fullCompile 'com.google.android.gms:play-services-fitness:11.4.2'
freeCompile 'com.google.android.gms:play-services-ads:11.4.2'
//noinspection GradleDynamicVersion
compile 'ch.acra:acra:4.9.0'
compile 'com.github.amlcurran.showcaseview:library:5.4.3'
compile 'com.nononsenseapps:filepicker:3.0.0'
compile 'com.nononsenseapps:filepicker:4.1.0'
//wearApp project(':MeditationAssistantWear')
// TODO: Uncomment when Wear app is ready
compile 'com.android.support:recyclerview-v7:24.2.1'
}
}

View File

@ -183,10 +183,6 @@
-keep,allowshrinking,allowoptimization class * { <methods>; }
-keepclasseswithmembernames,allowshrinking class * {
native <methods>;
}
-keepclasseswithmembers,allowshrinking class * {
public <init>(android.content.Context,android.util.AttributeSet);
public <init>(android.content.Context,android.util.AttributeSet,int);

View File

@ -11,8 +11,6 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
@ -69,6 +67,15 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="sh.ftp.rocketninelabs.meditationassistant.MainActivity"/>
</activity>
<activity
android:name="sh.ftp.rocketninelabs.meditationassistant.AuthResultActivity"
android:configChanges="keyboard|keyboardHidden|screenSize|orientation"
android:label="@string/signInToMediNET"
android:launchMode="singleTop">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="sh.ftp.rocketninelabs.meditationassistant.MainActivity"/>
</activity>
<activity
android:name="sh.ftp.rocketninelabs.meditationassistant.MediNETActivity"
android:configChanges="keyboard|keyboardHidden|screenSize|orientation"

View File

@ -141,13 +141,13 @@ public class AboutActivity extends Activity {
public void openHowToMeditate(View view) {
startActivity(new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://medinet.ftp.sh/howtomeditate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
Uri.parse("https://medinet.rocketnine.space/howtomeditate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
public void openTranslate(View view) {
startActivity(new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://medinet.ftp.sh/translate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
Uri.parse("https://medinet.rocketnine.space/translate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
public void openDonate(View view) {
@ -192,7 +192,7 @@ public class AboutActivity extends Activity {
} else {
startActivity(new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://medinet.ftp.sh/donate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
Uri.parse("https://rocketnine.space/donate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}

View File

@ -0,0 +1,173 @@
package sh.ftp.rocketninelabs.meditationassistant;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import net.openid.appauth.connectivity.ConnectionBuilder;
import net.openid.appauth.connectivity.DefaultConnectionBuilder;
import org.json.JSONObject;
import java.lang.ref.WeakReference;
/**
* Reads and validates the demo app configuration from `res/raw/auth_config.json`. Configuration
* changes are detected by comparing the hash of the last known configuration to the read
* configuration. When a configuration change is detected, the app state is reset.
*/
public final class AuthConfiguration {
private static final String TAG = "Configuration";
private static final String PREFS_NAME = "config";
private static final String KEY_LAST_HASH = "lastHash";
private static WeakReference<AuthConfiguration> sInstance = new WeakReference<>(null);
private final Context mContext;
private final SharedPreferences mPrefs;
private final Resources mResources;
private JSONObject mConfigJson;
private String mConfigError;
private String mClientId;
private String mScope;
private Uri mRedirectUri;
private Uri mDiscoveryUri;
private Uri mAuthEndpointUri;
private Uri mTokenEndpointUri;
private Uri mRegistrationEndpointUri;
public static AuthConfiguration getInstance(Context context) {
AuthConfiguration config = sInstance.get();
if (config == null) {
config = new AuthConfiguration(context);
sInstance = new WeakReference<AuthConfiguration>(config);
}
return config;
}
public AuthConfiguration(Context context) {
mContext = context;
mPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
mResources = context.getResources();
try {
readConfiguration();
} catch (InvalidConfigurationException ex) {
mConfigError = ex.getMessage();
}
}
/**
* Indicates whether the current configuration is valid.
*/
public boolean isValid() {
return mConfigError == null;
}
/**
* Returns a description of the configuration error, if the configuration is invalid.
*/
@Nullable
public String getConfigurationError() {
return mConfigError;
}
@Nullable
public String getClientId() {
return mClientId;
}
@NonNull
public String getScope() {
return mScope;
}
@NonNull
public Uri getRedirectUri() {
return mRedirectUri;
}
@Nullable
public Uri getDiscoveryUri() {
return mDiscoveryUri;
}
@Nullable
public Uri getAuthEndpointUri() {
return mAuthEndpointUri;
}
@Nullable
public Uri getTokenEndpointUri() {
return mTokenEndpointUri;
}
@Nullable
public Uri getRegistrationEndpointUri() {
return mRegistrationEndpointUri;
}
public ConnectionBuilder getConnectionBuilder() {
return DefaultConnectionBuilder.INSTANCE;
}
private void readConfiguration() throws InvalidConfigurationException {
mClientId = ""; // TODO: blank?
mScope = "openid email profile";
mRedirectUri = Uri.parse("https://medinet.rocketnine.space/oauth");
if (!isRedirectUriRegistered()) {
throw new InvalidConfigurationException(
"redirect_uri is not handled by any activity in this app! "
+ "Ensure that the appAuthRedirectScheme in your build.gradle file "
+ "is correctly configured, or that an appropriate intent filter "
+ "exists in your app manifest.");
}
/*if (getConfigString("discovery_uri") == null) {
mAuthEndpointUri = getRequiredConfigWebUri("authorization_endpoint_uri");
mTokenEndpointUri = getRequiredConfigWebUri("token_endpoint_uri");
if (mClientId == null) {
mRegistrationEndpointUri = getRequiredConfigWebUri("registration_endpoint_uri");
}
} else {
mDiscoveryUri = getRequiredConfigWebUri("discovery_uri");
}*/
mDiscoveryUri = Uri.parse("");
}
private boolean isRedirectUriRegistered() {
// ensure that the redirect URI declared in the configuration is handled by some activity
// in the app, by querying the package manager speculatively
Intent redirectIntent = new Intent();
redirectIntent.setPackage(mContext.getPackageName());
redirectIntent.setAction(Intent.ACTION_VIEW);
redirectIntent.addCategory(Intent.CATEGORY_BROWSABLE);
redirectIntent.setData(mRedirectUri);
return !mContext.getPackageManager().queryIntentActivities(redirectIntent, 0).isEmpty();
}
public static final class InvalidConfigurationException extends Exception {
InvalidConfigurationException(String reason) {
super(reason);
}
InvalidConfigurationException(String reason, Throwable cause) {
super(reason, cause);
}
}
}

View File

@ -0,0 +1,225 @@
package sh.ftp.rocketninelabs.meditationassistant;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import net.openid.appauth.AppAuthConfiguration;
import net.openid.appauth.AuthState;
import net.openid.appauth.AuthorizationException;
import net.openid.appauth.AuthorizationResponse;
import net.openid.appauth.AuthorizationService;
import net.openid.appauth.AuthorizationServiceDiscovery;
import net.openid.appauth.ClientAuthentication;
import net.openid.appauth.TokenRequest;
import net.openid.appauth.TokenResponse;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import okio.Okio;
/**
* Displays the authorized state of the user. This activity is provided with the outcome of the
* authorization flow, which it uses to negotiate the final authorized state,
* by performing an authorization code exchange if necessary. After this, the activity provides
* additional post-authorization operations if available, such as fetching user info and refreshing
* access tokens.
*/
public class AuthResultActivity extends Activity {
MeditationAssistant ma = null;
private static final String KEY_USER_INFO = "userInfo";
private AuthorizationService mAuthService;
private AuthStateManager mStateManager;
private final AtomicReference<JSONObject> mUserInfoJson = new AtomicReference<>();
private ExecutorService mExecutor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStateManager = AuthStateManager.getInstance(this);
mExecutor = Executors.newSingleThreadExecutor();
AuthConfiguration config = AuthConfiguration.getInstance(this);
mAuthService = new AuthorizationService(
this,
new AppAuthConfiguration.Builder()
.setConnectionBuilder(config.getConnectionBuilder())
.build());
displayLoading("Restoring state...");
if (savedInstanceState != null) {
try {
mUserInfoJson.set(new JSONObject(savedInstanceState.getString(KEY_USER_INFO)));
} catch (JSONException ex) {
Log.e("MA", "Failed to parse saved user info JSON, discarding", ex);
}
}
}
@Override
protected void onStart() {
super.onStart();
if (mExecutor.isShutdown()) {
mExecutor = Executors.newSingleThreadExecutor();
}
if (mStateManager.getCurrent().isAuthorized()) {
updateState();
return;
}
// the stored AuthState is incomplete, so check if we are currently receiving the result of
// the authorization flow from the browser.
AuthorizationResponse response = AuthorizationResponse.fromIntent(getIntent());
AuthorizationException ex = AuthorizationException.fromIntent(getIntent());
if (response != null || ex != null) {
mStateManager.updateAfterAuthorization(response, ex);
}
if (response != null && response.authorizationCode != null) {
// authorization code exchange is required
mStateManager.updateAfterAuthorization(response, ex);
exchangeAuthorizationCode(response);
} else if (ex != null) {
// TODO: handle failure "Authorization flow failed: " + ex.getMessage());
} else {
// TODO: handle failure No authorization state retained - reauthorization required");
}
}
@Override
protected void onSaveInstanceState(Bundle state) {
// user info is retained to survive activity restarts, such as when rotating the
// device or switching apps. This isn't essential, but it helps provide a less
// jarring UX when these events occur - data does not just disappear from the view.
if (mUserInfoJson.get() != null) {
state.putString(KEY_USER_INFO, mUserInfoJson.toString());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mAuthService.dispose();
mExecutor.shutdownNow();
}
@MainThread
private void displayLoading(String message) {
Log.d("MA", "Auth: " + message);
}
@MainThread
private void updateState() {
AuthState state = mStateManager.getCurrent();
if (state.getAccessToken() == null) {
Log.d("MA", "Access token was null: ");
// TODO: Handle auth failure, prompt to retry
finish();
return;
}
Long expiresAt = state.getAccessTokenExpirationTime();
if (expiresAt != null && expiresAt < System.currentTimeMillis()) {
refreshAccessToken();
return;
}
Log.d("MA", "Got token");
getMeditationAssistant().getMediNET().signInWithAuthToken(state.getAccessToken());
finish();
}
@MainThread
private void refreshAccessToken() {
displayLoading("Refreshing access token");
performTokenRequest(
mStateManager.getCurrent().createTokenRefreshRequest(),
this::handleAccessTokenResponse);
}
@MainThread
private void exchangeAuthorizationCode(AuthorizationResponse authorizationResponse) {
displayLoading("Exchanging authorization code");
performTokenRequest(
authorizationResponse.createTokenExchangeRequest(),
this::handleCodeExchangeResponse);
}
@MainThread
private void performTokenRequest(
TokenRequest request,
AuthorizationService.TokenResponseCallback callback) {
ClientAuthentication clientAuthentication;
try {
clientAuthentication = mStateManager.getCurrent().getClientAuthentication();
} catch (ClientAuthentication.UnsupportedAuthenticationMethod ex) {
Log.d("MA", "Token request cannot be made, client authentication for the token "
+ "endpoint could not be constructed (%s)", ex);
// TODO handle failure Client authentication method is unsupported");
return;
}
mAuthService.performTokenRequest(
request,
clientAuthentication,
callback);
}
@WorkerThread
private void handleAccessTokenResponse(
@Nullable TokenResponse tokenResponse,
@Nullable AuthorizationException authException) {
mStateManager.updateAfterTokenResponse(tokenResponse, authException);
runOnUiThread(this::updateState);
}
@WorkerThread
private void handleCodeExchangeResponse(
@Nullable TokenResponse tokenResponse,
@Nullable AuthorizationException authException) {
mStateManager.updateAfterTokenResponse(tokenResponse, authException);
if (!mStateManager.getCurrent().isAuthorized()) {
final String message = "Authorization Code exchange failed"
+ ((authException != null) ? authException.error : "");
// WrongThread inference is incorrect for lambdas
//noinspection WrongThread
// TODO: Handle not authorized, show error
} else {
runOnUiThread(this::updateState);
}
}
public MeditationAssistant getMeditationAssistant() {
if (ma == null) {
ma = (MeditationAssistant) this.getApplication();
}
return ma;
}
}

View File

@ -0,0 +1,154 @@
package sh.ftp.rocketninelabs.meditationassistant;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.AnyThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import net.openid.appauth.AuthState;
import net.openid.appauth.AuthorizationException;
import net.openid.appauth.AuthorizationResponse;
import net.openid.appauth.RegistrationResponse;
import net.openid.appauth.TokenResponse;
import org.json.JSONException;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/**
* An example persistence mechanism for an {@link AuthState} instance.
* This stores the instance in a shared preferences file, and provides thread-safe access and
* mutation.
*/
public class AuthStateManager {
private static final AtomicReference<WeakReference<AuthStateManager>> INSTANCE_REF =
new AtomicReference<>(new WeakReference<AuthStateManager>(null));
private static final String TAG = "AuthStateManager";
private static final String STORE_NAME = "AuthState";
private static final String KEY_STATE = "state";
private final SharedPreferences mPrefs;
private final ReentrantLock mPrefsLock;
private final AtomicReference<AuthState> mCurrentAuthState;
@AnyThread
public static AuthStateManager getInstance(@NonNull Context context) {
AuthStateManager manager = INSTANCE_REF.get().get();
if (manager == null) {
manager = new AuthStateManager(context.getApplicationContext());
INSTANCE_REF.set(new WeakReference<>(manager));
}
return manager;
}
private AuthStateManager(Context context) {
mPrefs = context.getSharedPreferences(STORE_NAME, Context.MODE_PRIVATE);
mPrefsLock = new ReentrantLock();
mCurrentAuthState = new AtomicReference<>();
}
@AnyThread
@NonNull
public AuthState getCurrent() {
if (mCurrentAuthState.get() != null) {
return mCurrentAuthState.get();
}
AuthState state = readState();
if (mCurrentAuthState.compareAndSet(null, state)) {
return state;
} else {
return mCurrentAuthState.get();
}
}
@AnyThread
@NonNull
public AuthState replace(@NonNull AuthState state) {
writeState(state);
mCurrentAuthState.set(state);
return state;
}
@AnyThread
@NonNull
public AuthState updateAfterAuthorization(
@Nullable AuthorizationResponse response,
@Nullable AuthorizationException ex) {
AuthState current = getCurrent();
current.update(response, ex);
return replace(current);
}
@AnyThread
@NonNull
public AuthState updateAfterTokenResponse(
@Nullable TokenResponse response,
@Nullable AuthorizationException ex) {
AuthState current = getCurrent();
current.update(response, ex);
return replace(current);
}
@AnyThread
@NonNull
public AuthState updateAfterRegistration(
RegistrationResponse response,
AuthorizationException ex) {
AuthState current = getCurrent();
if (ex != null) {
return current;
}
current.update(response);
return replace(current);
}
@AnyThread
@NonNull
private AuthState readState() {
mPrefsLock.lock();
try {
String currentState = mPrefs.getString(KEY_STATE, null);
if (currentState == null) {
return new AuthState();
}
try {
return AuthState.jsonDeserialize(currentState);
} catch (JSONException ex) {
Log.w(TAG, "Failed to deserialize stored auth state - discarding");
return new AuthState();
}
} finally {
mPrefsLock.unlock();
}
}
@AnyThread
private void writeState(@Nullable AuthState state) {
mPrefsLock.lock();
try {
SharedPreferences.Editor editor = mPrefs.edit();
if (state == null) {
editor.remove(KEY_STATE);
} else {
editor.putString(KEY_STATE, state.jsonSerializeString());
}
if (!editor.commit()) {
throw new IllegalStateException("Failed to write state to shared prefs");
}
} finally {
mPrefsLock.unlock();
}
}
}

View File

@ -341,7 +341,7 @@ public class CompleteActivity extends Activity {
public void postMediNET(View view) {
if (getMeditationAssistant().getMediNETKey() == "") {
getMeditationAssistant().showSignInDialog(this);
getMeditationAssistant().startAuth(true);
return;
}

View File

@ -20,7 +20,7 @@ public class JavaScriptInterface implements JavascriptCallback {
MeditationAssistant ma = (MeditationAssistant) this.activity
.getApplication();
this.activity.finish();
ma.getMediNET().askToSignIn();
ma.startAuth(false);
}
@JavascriptInterface

View File

@ -9,7 +9,6 @@ import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.preference.ListPreference;
import android.util.AttributeSet;
import android.util.Log;

View File

@ -21,7 +21,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
@ -63,7 +62,6 @@ public class MainActivity extends Activity implements OnShowcaseEventListener {
public static int ID_DELAY = 77702;
public static int ID_INTERVAL = 77701;
public static int ID_END = 77703;
private static final int PERMISSION_REQUEST_GET_ACCOUNTS = 3001;
public MeditationAssistant ma = null;
SharedPreferences.OnSharedPreferenceChangeListener sharedPrefslistener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@ -494,7 +492,7 @@ public class MainActivity extends Activity implements OnShowcaseEventListener {
case DialogInterface.BUTTON_POSITIVE:
startActivity(new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://medinet.ftp.sh/translate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
Uri.parse("https://medinet.rocketnine.space/translate")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
break;
@ -2467,14 +2465,8 @@ public class MainActivity extends Activity implements OnShowcaseEventListener {
)
)
.setTitle(
getString(R.string.signOutOfMediNETConfirmTitle))
.setMessage(
String.format(
getString(R.string.signOutOfMediNETConfirm),
getMeditationAssistant()
.getMediNETProvider()
)
)
getString(R.string.signOut))
.setMessage(getString(R.string.signOutOfMediNETConfirmTitle))
.setPositiveButton(getString(R.string.signOut),
dialogClickListener)
.setNegativeButton(getString(R.string.cancel),
@ -2486,15 +2478,7 @@ public class MainActivity extends Activity implements OnShowcaseEventListener {
}
public void askToSignIn() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.GET_ACCOUNTS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.GET_ACCOUNTS},
PERMISSION_REQUEST_GET_ACCOUNTS);
} else {
getMeditationAssistant().getMediNET().askToSignIn();
}
getMeditationAssistant().startAuth(false);
}
public void stopMediaPlayer() {
@ -2548,49 +2532,4 @@ public class MainActivity extends Activity implements OnShowcaseEventListener {
public void onShowcaseViewTouchBlocked(MotionEvent motionEvent) {
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_GET_ACCOUNTS: {
if ((grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) || !ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.GET_ACCOUNTS)) {
getMeditationAssistant().getMediNET().askToSignIn();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(
getResources()
.getDrawable(
getTheme()
.obtainStyledAttributes(
getMeditationAssistant()
.getMATheme(true),
new int[]{R.attr.actionIconSettings}
)
.getResourceId(0, 0)
)
)
.setTitle(getString(R.string.permissionRequest))
.setMessage(
getString(R.string.permissionRequestAccounts))
.setPositiveButton(getString(R.string.tryAgain),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
askToSignIn();
}
})
.setNegativeButton(getString(R.string.deny),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
getMeditationAssistant().getMediNET().askToSignIn();
}
}).show();
}
}
}
}
}

View File

@ -19,11 +19,11 @@ import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* Created by root on 11/2/13.
*/
public class MediNET {
public static Integer version = 5;
public static Integer version = 6;
// API v1-5 was used on pre Android 4.1 (discontinued) releases
// API v6 signifies non-discontinued (1.4.1+, Android 4.1+) app version
public String status = "disconnected";
public String result = "";
public MainActivity activity;
@ -71,46 +71,6 @@ public class MediNET {
return String.valueOf(hours) + ":" + String.format("%02d", minutes);
}
public void askToSignIn() {
/*if (activity == null || activity.stopped) {
Log.d("MeditationAssistant",
"MainActivity null or stopped, restarting... Stopped: "
+ activity.stopped.toString());*/
if (activity == null) {
Intent openActivity = new Intent(getMeditationAssistant()
.getApplicationContext(), MainActivity.class);
openActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
openActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
handler.postDelayed(new Runnable() {
@Override
public void run() {
Log.d("MeditationAssistant",
"Open MainActivity runnable is now running...");
askToSignIn();
}
}, 400);
getMeditationAssistant().getApplicationContext().startActivity(
openActivity);
return;
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (activity == null) {
Log.d("MeditationAssistant",
"askToSignIn activity is null, returning...");
return;
}
getMeditationAssistant().showSignInDialog(activity);
}
});
}
public void browseTo(MainActivity act, String page) {
activity = act;
browsetopage = page;
@ -129,7 +89,7 @@ public class MediNET {
}
});
} else {
askToSignIn();
getMeditationAssistant().startAuth(false);
}
}
@ -150,7 +110,7 @@ public class MediNET {
}
if (getMeditationAssistant().getMediNETKey().equals("")) {
askToSignIn();
getMeditationAssistant().startAuth(false);
return false;
}

View File

@ -33,7 +33,6 @@ public class MediNETActivity extends Activity {
private String provider = "";
private MeditationAssistant ma = null;
private Handler handler = new Handler();
private boolean signing_in = false;
private boolean hide_refresh = false;
public static Intent newEmailIntent(Context context, String address, String subject, String body, String cc) {
@ -57,7 +56,7 @@ public class MediNETActivity extends Activity {
TimeZone tz = TimeZone.getDefault();
Date now = new Date();
return "https://medinet.ftp.sh/client_android.php?v="
return "https://medinet.rocketnine.space/client_android.php?v="
+ MediNET.version.toString() + "&avn="
+ String.valueOf(getMeditationAssistant().getMAAppVersionNumber()) + "&page=" + page + "&th="
+ ma.getMAThemeString() + "&tz="
@ -68,20 +67,8 @@ public class MediNETActivity extends Activity {
public void goTo(String go_to) {
String url;
if (go_to.equals("Google") || go_to.equals("Facebook")
|| go_to.equals("Twitter") || go_to.equals("AOL")
|| go_to.equals("OpenID") || go_to.equals("Live")
|| go_to.equals("LinkedIn")) {
setTitle(String.format(getString(R.string.signInWithProvider),
go_to));
url = "https://medinet.ftp.sh/client_android_login.php?v="
+ MediNET.version.toString() + "&avn="
+ String.valueOf(getMeditationAssistant().getMAAppVersionNumber()) + "&provider=" + go_to;
provider = go_to;
signing_in = true;
} else if (go_to.equals("gpl") || go_to.equals("lgpl")) {
if (go_to.equals("gpl") || go_to.equals("lgpl")) {
setTitle("");
signing_in = true;
hide_refresh = true;
if (go_to.equals("gpl")) {
@ -90,24 +77,30 @@ public class MediNETActivity extends Activity {
url = "file:///android_asset/lgpl.html";
}
} else {
if (go_to.equals("community")) {
setTitle(getString(R.string.community));
} else if (go_to.equals("sessions")) {
setTitle(getString(R.string.sessions));
} else if (go_to.equals("account")) {
setTitle(getString(R.string.account));
} else if (go_to.equals("forum")) {
setTitle(getString(R.string.forum));
} else if (go_to.equals("groups")) {
setTitle(getString(R.string.groups));
} else if (go_to.equals("signout")) {
switch (go_to) {
case "community":
setTitle(getString(R.string.community));
break;
case "sessions":
setTitle(getString(R.string.sessions));
break;
case "account":
setTitle(getString(R.string.account));
break;
case "forum":
setTitle(getString(R.string.forum));
break;
case "groups":
setTitle(getString(R.string.groups));
break;
case "signout":
} else {
return;
break;
default:
return;
}
url = getPageUrl(go_to);
signing_in = false;
}
Log.d("MeditationAssistant", go_to + " - Going to: " + url);
@ -200,12 +193,11 @@ public class MediNETActivity extends Activity {
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
if (Uri.parse(url) != null && Uri.parse(url).getHost() != null && Uri.parse(url).getHost().equals("medinet.ftp.sh")) {
if (Uri.parse(url) != null && Uri.parse(url).getHost() != null && Uri.parse(url).getHost().equals("medinet.rocketnine.space")) {
if (webView.getTitle() != null
&& !webView.getTitle().trim().equals("")) {
setTitle(webView.getTitle());
}
signing_in = url.contains("/hybridauth/");
} else {
/*
* setTitle(String.format(getString(R.string.
@ -219,36 +211,15 @@ public class MediNETActivity extends Activity {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
/*getMeditationAssistant().setWebViewScale(
(int) (100 * view.getScale()));*/
Log.d("MeditationAssistant",
"Signing_in: " + String.valueOf(signing_in) + " - "
+ url
);
if (url != null && url.startsWith("mailto:")) {
MailTo mt = MailTo.parse(url);
Intent i = newEmailIntent(MediNETActivity.this, mt.getTo(), mt.getSubject(), mt.getBody(), mt.getCc());
startActivity(i);
view.reload();
return true;
/*} else if (url != null && Uri.parse(url) != null && Uri.parse(url).getHost() != null
&& !Uri.parse(url).getHost()
.equals("medinet.ftp.sh")
&& webView.getUrl() != null
&& Uri.parse(webView.getUrl()).getHost() != null && Uri.parse(webView.getUrl()).getHost()
.equals("medinet.ftp.sh")
&& !webView.getUrl().contains("provider=OpenID")) {
Log.d("MA", "!!!!!!!!!!!!!!!!! OPENING!!!");
Intent browserIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse(url));
startActivity(browserIntent);*/
} else {
view.loadUrl(url);
}
Log.d("MA", Uri.parse(url).toString());
Log.d("MA", Uri.parse(url).getHost());
return true;
}
});
@ -275,16 +246,7 @@ public class MediNETActivity extends Activity {
// webView.getSettings().setDefaultZoom(WebSettings.ZoomDensity.FAR);
if (activityOnCreate) {
if (getIntent().hasExtra("provider")) {
provider = getIntent().getStringExtra("provider");
if (provider.equals("Google") || provider.equals("Facebook")
|| provider.equals("Twitter") || provider.equals("AOL")
|| provider.equals("OpenID") || provider.equals("Live")
|| provider.equals("LinkedIn")) {
signing_in = true;
goTo(provider);
}
} else if (getIntent().hasExtra("page")
if (getIntent().hasExtra("page")
&& (getIntent().getStringExtra("page").equals("community")
|| getIntent().getStringExtra("page").equals(
"sessions")
@ -296,7 +258,6 @@ public class MediNETActivity extends Activity {
"forum")
|| getIntent().getStringExtra("page").equals("gpl") || getIntent()
.getStringExtra("page").equals("lgpl"))) {
signing_in = false;
goTo(getIntent().getStringExtra("page"));
}
} else {
@ -414,12 +375,6 @@ public class MediNETActivity extends Activity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (signing_in) {
menu.findItem(R.id.menuMediNET).setVisible(false);
} else {
menu.findItem(R.id.menuMediNET).setVisible(true);
}
if (hide_refresh) {
menu.findItem(R.id.refreshMediNET).setVisible(false);
} else {
@ -475,16 +430,7 @@ public class MediNETActivity extends Activity {
getResources().getDrawable(
android.R.drawable.background_holo_dark));*/
webView.getSettings();
if (signing_in) {
webView.setBackgroundColor(Color.WHITE);
} else {
if (Build.VERSION.SDK_INT < 11) {
webView.setBackgroundColor(Color.TRANSPARENT);
} else {
// Fix background flicker
webView.setBackgroundColor(Color.argb(1, 0, 0, 0));
}
}
webView.setBackgroundColor(Color.argb(1, 0, 0, 0)); // TODO: Is this still necessary?
}
}
}

View File

@ -15,6 +15,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
@ -50,7 +51,7 @@ public class MediNETTask extends AsyncTask<MediNET, Integer, MediNET> {
String appVersion = getMeditationAssistant().getMAAppVersion() + BuildConfig.FLAVOR;
if (this.nextURL == null) {
this.nextURL = "https://medinet.ftp.sh/client_android.php?v="
this.nextURL = "https://medinet.rocketnine.space/client_android.php?v="
+ MediNET.version.toString() + "&av="
+ appVersion + "&am="
+ getMeditationAssistant().getMarketName() + "&avn="
@ -59,7 +60,7 @@ public class MediNETTask extends AsyncTask<MediNET, Integer, MediNET> {
}
if (action.equals("signin")) {
this.nextURL = "https://medinet.ftp.sh/client_android_login_oauth2.php?v="
this.nextURL = "https://medinet.rocketnine.space/client_android_login_oauth2.php?v="
+ MediNET.version.toString() + "&av="
+ appVersion + "&avn="
+ String.valueOf(getMeditationAssistant().getMAAppVersionNumber()) + "&tz="
@ -130,6 +131,11 @@ public class MediNETTask extends AsyncTask<MediNET, Integer, MediNET> {
getMeditationAssistant().longToast(getMeditationAssistant().getString(R.string.sessionNotPosted));
e.printStackTrace();
}
try {
Log.i("MA", "Post data: " + getMeditationAssistant().getPostDataString(postData));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
/*
@ -152,8 +158,8 @@ public class MediNETTask extends AsyncTask<MediNET, Integer, MediNET> {
try {
URL medinetURL = new URL(this.nextURL);
medinetConnection = (HttpURLConnection) medinetURL.openConnection();
medinetConnection.setChunkedStreamingMode(0);
medinetConnection.setReadTimeout(10000);
medinetConnection.setConnectTimeout(15000);
medinetConnection.setRequestMethod("POST");
medinetConnection.setDoInput(true);
medinetConnection.setDoOutput(true);
@ -162,12 +168,11 @@ public class MediNETTask extends AsyncTask<MediNET, Integer, MediNET> {
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(getMeditationAssistant().getPostDataString(postData));
medinetConnection.connect();
writer.flush();
writer.close();
os.close();
medinetConnection.connect();
int responseCode = medinetConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
@ -200,7 +205,7 @@ public class MediNETTask extends AsyncTask<MediNET, Integer, MediNET> {
if (medinetConnection.getHeaderField("x-MediNET") != null) {
if (medinetConnection.getHeaderField("x-MediNET")
.equals("signin")) {
this.medinet.askToSignIn();
getMeditationAssistant().startAuth(false);
} else {
if (action.equals("signin") && medinetConnection.getHeaderField("x-MediNET-Key") != null) { /* Oauth2 sign in */
Log.d("MeditationAssistant", "Header key: "

View File

@ -1,6 +1,5 @@
package sh.ftp.rocketninelabs.meditationassistant;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
@ -23,11 +22,10 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.provider.Settings;
@ -41,6 +39,11 @@ import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.Toast;
import net.openid.appauth.AuthorizationRequest;
import net.openid.appauth.AuthorizationService;
import net.openid.appauth.AuthorizationServiceConfiguration;
import net.openid.appauth.ResponseTypeValues;
import org.acra.ACRA;
import org.acra.annotation.ReportsCrashes;
@ -58,7 +61,7 @@ import java.util.Map;
import java.util.TimeZone;
@ReportsCrashes(
formUri = "https://medinet.ftp.sh/acra/acra.php"
formUri = "https://medinet.rocketnine.space/acra/acra.php"
)
public class MeditationAssistant extends Application {
@ -66,6 +69,7 @@ public class MeditationAssistant extends Application {
public static String ACTION_PRESET = "sh.ftp.rocketninelabs.meditationassistant.PRESET";
public static String ACTION_REMINDER = "sh.ftp.rocketninelabs.meditationassistant.DAILY_NOTIFICATION";
public static String ACTION_UPDATED = "sh.ftp.rocketninelabs.meditationassistant.DAILY_NOTIFICATION_UPDATED";
public static String ACTION_AUTH = "sh.ftp.rocketninelabs.meditationassistant.AUTH";
public static int REQUEST_FIT = 22;
public static int MEDIA_DELAY = 1000;
public Boolean debug_widgets = false; // Debug
@ -227,7 +231,7 @@ public class MeditationAssistant extends Application {
return mNotificationManager.getCurrentInterruptionFilter();
}
return -1;
return 0;
}
public String getDurationFormatted() {
@ -333,6 +337,37 @@ public class MeditationAssistant extends Application {
return medinetprovider;
}
public void startAuth(boolean showToast) {
if (showToast) {
shortToast(getString(R.string.signInToMediNET));
}
AsyncTask.execute(() -> {
AuthorizationServiceConfiguration serviceConfiguration = new AuthorizationServiceConfiguration(
Uri.parse("https://accounts.google.com/o/oauth2/v2/auth") /* auth endpoint */,
Uri.parse("https://www.googleapis.com/oauth2/v4/token") /* token endpoint */
);
String clientId = BuildConfig.GOOGLEOAUTHKEY;
Uri redirectUri = Uri.parse(BuildConfig.APPLICATION_ID + ":/oauth");
AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder(
serviceConfiguration,
clientId,
ResponseTypeValues.CODE,
redirectUri
);
builder.setScopes("profile");
AuthorizationRequest request = builder.build();
AuthorizationService authorizationService = new AuthorizationService(MeditationAssistant.this);
authorizationService.performAuthorizationRequest(
request,
PendingIntent.getActivity(MeditationAssistant.this, 0, new Intent(MeditationAssistant.this, AuthResultActivity.class), 0),
PendingIntent.getActivity(MeditationAssistant.this, 0, new Intent(MeditationAssistant.this, AuthResultActivity.class), 0));
});
}
public void recalculateMeditationStreak() {
Calendar dayCalendar = new GregorianCalendar();
Integer daysback = 0;
@ -1005,203 +1040,6 @@ public class MeditationAssistant extends Application {
notificationManager.notify(0, notification);
}
public AlertDialog showSignInDialog(final Activity activity) {
signin_activity = activity;
AccountManager accountManager = AccountManager
.get(getApplicationContext());
final Account[] accounts = accountManager
.getAccountsByType("com.google");
final int size = accounts.length;
if (size > 0) {
String[] names = new String[size + 1];
int i = 0;
for (i = 0; i < size; i++) {
names[i] = accounts[i].name;
}
names[i] = getString(R.string.signInWithOpenID);
AlertDialog accountsAlertDialog = new AlertDialog.Builder(signin_activity)
.setTitle(getString(R.string.signInWith))
.setItems(names, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which != size) {
AccountManager am = AccountManager.get(getApplicationContext());
if (am != null) {
am.getAuthToken(accounts[which], AUTH_TOKEN_TYPE, null, signin_activity, new OnTokenAcquired(), new Handler(new Handler.Callback() {
public boolean handleMessage(Message msg) {
Log.d("MeditationAssistant", "on error: " + msg.what);
shortToast(getString(R.string.signInGoogleError));
return false;
}
}));
}
} else {
showOpenIDSignInDialog(signin_activity);
}
}
})
.create();
accountsAlertDialog.show();
return accountsAlertDialog;
} else {
return showOpenIDSignInDialog(signin_activity);
}
}
public AlertDialog showOpenIDSignInDialog(Activity activity) {
if (alertDialog != null && alertDialog.getOwnerActivity() != null
&& alertDialog.getOwnerActivity() == activity) {
Log.d("MeditationAssistant",
"Attempting to reuse MediNET sign in dialog");
try {
if (!alertDialog.isShowing()) {
alertDialog.show();
}
Log.d("MeditationAssistant", "Reusing MediNET sign in dialog");
return alertDialog;
} catch (WindowManager.BadTokenException e) {
// Activity is not in the foreground
}
}
int[] buttons = {R.id.btnGoogle, R.id.btnFacebook, R.id.btnAOL,
R.id.btnTwitter, R.id.btnLive, R.id.btnOpenID};
View view = LayoutInflater.from(activity).inflate(
R.layout.medinet_signin,
(ViewGroup) activity.findViewById(R.id.medinetsignin_root));
for (int buttonid : buttons) {
ImageButton btn = (ImageButton) view.findViewById(buttonid);
if (btn.getId() == R.id.btnGoogle) {
if (!getMAThemeString().equals("dark")) {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_google_light));
} else {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_google));
}
} else if (btn.getId() == R.id.btnFacebook) {
if (!getMAThemeString().equals("dark")) {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_facebook_light));
} else {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_facebook));
}
} else if (btn.getId() == R.id.btnAOL) {
if (!getMAThemeString().equals("dark")) {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_aol_light));
} else {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_aol));
}
} else if (btn.getId() == R.id.btnTwitter) {
if (!getMAThemeString().equals("dark")) {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_twitter_light));
} else {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_twitter));
}
} else if (btn.getId() == R.id.btnLive) {
if (!getMAThemeString().equals("dark")) {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_live_light));
} else {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_live));
}
} else if (btn.getId() == R.id.btnOpenID) {
if (!getMAThemeString().equals("dark")) {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_openid_light));
} else {
btn.setImageDrawable(getResources().getDrawable(
R.drawable.logo_openid));
}
}
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageButton img = (ImageButton) v;
Intent intent = new Intent(getApplicationContext(),
MediNETActivity.class);
if (img.getId() == R.id.btnGoogle) {
intent.putExtra("provider", "Google");
} else if (img.getId() == R.id.btnFacebook) {
intent.putExtra("provider", "Facebook");
} else if (img.getId() == R.id.btnAOL) {
intent.putExtra("provider", "AOL");
} else if (img.getId() == R.id.btnTwitter) {
intent.putExtra("provider", "Twitter");
} else if (img.getId() == R.id.btnLive) {
intent.putExtra("provider", "Live");
} else if (img.getId() == R.id.btnOpenID) {
intent.putExtra("provider", "OpenID");
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (alertDialog != null) {
try {
alertDialog.dismiss();
} catch (Exception e) {
// Do nothing
}
}
startActivity(intent);
}
});
}
alertDialog = new AlertDialog.Builder(activity)
.setView(view)
.setTitle(R.string.signInToMediNET)
.setIcon(
activity.getResources().getDrawable(
getTheme().obtainStyledAttributes(getMATheme(true),
new int[]{R.attr.actionIconForward})
.getResourceId(0, 0)
)
).create();
alertDialog
.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
if (getMediNETKey() == "") {
getMediNET().status = "disconnected";
getMediNET().updated();
}
}
});
alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
if (getMediNETKey() == "") {
getMediNET().status = "disconnected";
getMediNET().updated();
}
}
});
alertDialog.show();
return alertDialog;
}
public AlertDialog showStaleDataDialog() {
Log.d("MeditationAssistant", "Showing stale data dialog");
@ -1339,31 +1177,4 @@ public class MeditationAssistant extends Application {
GLOBAL_TRACKER, // Tracker used by all the apps from a company. eg: roll-up tracking.
ECOMMERCE_TRACKER, // Tracker used by all ecommerce transactions from a company.
}
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
@Override
public void run(AccountManagerFuture<Bundle> result) {
Intent launch = null;
try {
launch = (Intent) result.getResult().get(AccountManager.KEY_INTENT);
if (launch == null) {
String authtoken = result.getResult().getString(AccountManager.KEY_AUTHTOKEN);
if (!authtoken.equals("")) {
getMediNET().signInWithAuthToken(authtoken);
}
}
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
if (launch != null) {
signin_activity.startActivityForResult(launch, 0);
}
}
}
}

View File

@ -331,8 +331,7 @@ public class SettingsActivity extends PreferenceActivity {
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean isSimplePreferences(Context context) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
|| !isXLargeTablet(context);
return !isXLargeTablet(context);
}
@Override
@ -497,9 +496,7 @@ public class SettingsActivity extends PreferenceActivity {
public boolean onPreferenceClick(Preference arg0) {
if (getMeditationAssistant().getMediNETKey().equals("")) {
getMeditationAssistant().getMediNET().askToSignIn();
getMeditationAssistant().shortToast(
getString(R.string.signInToMediNET));
getMeditationAssistant().startAuth(true);
} else {
ArrayList<SessionSQL> sessionssql = getMeditationAssistant().db.getAllLocalSessions();
@ -529,9 +526,7 @@ public class SettingsActivity extends PreferenceActivity {
@Override
public boolean onPreferenceClick(Preference arg0) {
if (getMeditationAssistant().getMediNETKey().equals("")) {
getMeditationAssistant().getMediNET().askToSignIn();
getMeditationAssistant().shortToast(
getString(R.string.signInToImport));
getMeditationAssistant().startAuth(true);
} else {
if (getMeditationAssistant().getTimestamp()
- importsessions_lastlick > 5) {

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="8dp">
<ImageView
android:id="@+id/browser_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:contentDescription="@string/browser"/>
<TextView
android:id="@+id/browser_label"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"/>
</LinearLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -21,10 +21,11 @@
<string name="announcement">Ankündigung</string>
<!-- Permissions -->
<string name="permissionRequest">Berechtigungsanfrage</string>
<string name="permissionRequestAccounts">Die Berechtigung auf Ihre Google Account zugreifen zu dürfen ist als "Kontake" beachrieben. Ihre Kontake western nicht gelesen. Wenn Sie diesel Berechtigung akzeptieren müssen Sie um sich bei MediNET anzumelden Ihre Google Accountdaten nicht immer eingeben.</string>
<string name="permissionRequestReadExternal">Diesel Berechtigung wird benötigt um einen beliebigen Ton von Ihrem Great auszuwählen. </string>
<string name="permissionRequestNotificationControl">Bitte gewähren Sie auf dem nächsten Bildschirm Zugriff um die Funktion \'Nicht stören\' kontrollieren zu können.</string>
<string name="deny">Verweigern </string>
<string name="tryAgain">Erneut versuchen</string>
<!-- Main Activity -->
@ -104,7 +105,7 @@
<!-- Progress -->
<string name="calendar">Kalender</string>
<string name="statistics">Statistiken</string>
<string name="totalTimeSpentMeditating">Gesamte beim Meditieren verbrachte Zeit</string>
<string name="totalTimeSpentMeditating">Meditationsdauer ingesamt</string>
<string name="dayMondayShort">Mo</string>
<string name="dayTuesdayShort">Di</string>
<string name="dayWednesdayShort">Mi</string>
@ -203,6 +204,8 @@
<string name="extralarge">Riesig</string>
<string name="restartApp">Anwendung neustarten, um neue Einstellungen anzuwenden</string>
<string name="pref_notificationcontrol">Klingelton und Benachrichtigungen</string>
<string name="pref_notificationcontrol_priority">Priorität</string>
<string name="pref_notificationcontrol_alarms">Nur Alarme</string>
<string name="pref_notificationcontrol_vibrate">Einstellen auf Vibration</string>
<string name="pref_notificationcontrol_silent">Stummschalten</string>
<string name="pref_daily_reminder">Tägliche Erinnerung</string>

View File

@ -6,6 +6,7 @@
<string name="about">Acerca</string>
<string name="yes">Si</string>
<string name="no">No</string>
<string name="delete">Borrar</string>
<string name="sessions">Sesiones</string>
<string name="community">Comunidad</string>
@ -19,10 +20,19 @@
<string name="groups">Grupos</string>
<string name="announcement">Anuncio</string>
<!-- Permissions -->
<!-- Main Activity -->
<string name="timer">Temporizador</string>
<string name="cancel">Cancelar</string>
<string name="set">Establecer</string>
<string name="confirm">Confirmar</string>
@ -32,48 +42,70 @@
<string name="end">Ende</string>
<string name="pauseOrEnd">Pausa o ende</string>
<string name="resumeOrEnd">Sesión pausada, mantener para terminar</string>
<string name="mediNETConnecting">Conectando...</string>
<string name="mediNETConnected">Conectando a MediNET</string>
<string name="sessionInProgress">Sesión en progreso</string>
<string name="progress">Progreso</string>
<string name="timed">Terminar %s</string>
<string name="endAt">Terminar en</string>
<string name="presetLabelEndAt">Terminar %s</string>
<!-- About -->
<string name="version">Versión %s</string>
<string name="translate">Traducir</string>
<string name="howToMeditate">Como meditar</string>
<string name="share">Compartir</string>
<!--Short text displayed in sent application invitations [CHAR LIMIT=100] -->
<!-- Sessions -->
<string name="session">Sesión</string>
<!-- Session complete -->
<string name="sessionMessagePlaceholder">Mensaje</string>
<string name="aMoment">a momento</string>
<!-- Sign In -->
<!-- Progress -->
<string name="dayMondayShort">Lun</string>
<string name="dayTuesdayShort">Mar</string>
<string name="dayWednesdayShort">Mie</string>
@ -82,23 +114,145 @@
<string name="daySaturdayShort">Sab</string>
<string name="daySundayShort">Dom</string>
<string name="addSession">Añadir sesión</string>
<string name="started">Iniciado</string>
<string name="completed">Completado</string>
<!-- Settings -->
<!-- MediNET -->
<plurals name="numtimes">
<item quantity="one">%s time</item>
<item quantity="other">%s times</item>
</plurals>
<plurals name="sessionsImported">
<item quantity="one">%s sesión importado</item>
<item quantity="other">%s sesiones importado</item>
</plurals>
<plurals name="sessionsUploaded">
<item quantity="one">Uploaded %s session</item>
<item quantity="other">Uploaded %s sessions</item>
</plurals>
<plurals name="daysOfMeditationMinimal">
<item quantity="one">%d day</item>
<item quantity="other">%d days</item>
</plurals>
<plurals name="daysOfMeditation">
<item quantity="one">%d día de meditación</item>
<item quantity="other">%d días de meditación</item>
@ -108,6 +262,15 @@
<item quantity="other">días de meditación</item>
</plurals>
<!-- Widgets -->
<string name="widget1x1" tools:ignore="UnusedResources">Asistente de meditación 1x1</string>

View File

@ -0,0 +1,262 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="appName">Meditasjonsassistent Gratis</string>
<string name="appNameShort">Meditasjonsassistent</string>
<string name="about">Om</string>
<string name="yes">Ja</string>
<string name="no">Nei</string>
<string name="openWith">Åpne med</string>
<string name="delete">Slett</string>
<string name="sessions">Økter</string>
<string name="community">Fellesskap</string>
<string name="forum">Forum</string>
<string name="rate">Ranger</string>
<string name="refresh">Oppdater</string>
<string name="page">Side</string>
<string name="back">Tilbake</string>
<string name="forward">Fremover</string>
<string name="account">Konto</string>
<string name="groups">Grupper</string>
<string name="announcement">Kunngjøringer</string>
<!-- Permissions -->
<string name="permissionRequest">Forespørsel om tillatelse</string>
<string name="permissionRequestAccounts">Tillatelsen som gir tilgang til Google-kontoen din kalles "Kontakter". Kontaktene dine vil ikke bli lest.\n\nHvis du gir denne tilgangen slipper du å skrive inn brukernavnet og passordet til Google-kontoen din når du logger på MediNET</string>
<string name="permissionRequestReadExternal">Tillatelsen trengs for å kunne velge andre lydfiler fra enheten.</string>
<string name="permissionRequestNotificationControl">Vennligst gi tillatelse i neste steg for å kunne kontrollere "Ikke forstyrr"</string>
<string name="deny">Nekt</string>
<string name="tryAgain">Prøv igjen</string>
<!-- Main Activity -->
<string name="timer">Tidtaker</string>
<string name="timerHelp">Akkurat nå er tidtakeren stilt inn på 15 minutter. Du kan endre varigheten, sette den til å stoppe på et visst klokkeslett eller slå av alarmen helt. Trykk på tidtakeren nå for å se disse mulighetene.</string>
<string name="settingsHelp">Denne applikasjonen tilbyr mange funksjoner og tilpasningsmuligheter, blant annet å sette enheten i stillemodus automatisk og å gi deg daglig påminnelse om å meditere. Trykk på innstillingsikonet nå for å se disse mulighetene.</string>
<string name="medinetHelp">Logg på MediNET for å lagre øktene dine på nett. Hvis du gjør det kan du importere øktene dine igjen hvis de blir borte fra enheten din. Denne funksjonen er helt valgfri; å lagre øktene lokalt vil ikke gå ut over bruksopplevelsen.\n\nOg med det er denne introduksjonen over. Jeg håper at du får mye ut av Meditasjonsassistenten.</string>
<string name="cancel">Avbryt</string>
<string name="set">Sett</string>
<string name="confirm">Bekreft</string>
<string name="meditate">Mediter</string>
<string name="tapToSkip">Hopp over</string>
<string name="pause">Pause</string>
<string name="end">Avslutt</string>
<string name="pauseOrEnd">Pause eller avslutt</string>
<string name="resumeOrEnd">Gjenoppta eller avslutt</string>
<string name="pausedNotification">Økt satt på pause, hold for å avslutte</string>
<string name="mediNETConnecting">Kobler til...</string>
<string name="mediNETConnected">Koble til MediNET</string>
<string name="sessionInProgress">Økt pågår</string>
<string name="progress">Fremgang</string>
<string name="setPreset">Bruk forhåndsvalg</string>
<string name="setPresetHint">Hold for å bruke forhåndsvalg</string>
<!-- About -->
<!--Short text displayed in sent application invitations [CHAR LIMIT=100] -->
<!-- Sessions -->
<!-- Session complete -->
<!-- Sign In -->
<!-- Progress -->
<!-- Settings -->
<!-- MediNET -->
<!-- Widgets -->
</resources>

View File

@ -97,6 +97,8 @@
<string name="sessionDeletedLocally">Session deleted locally</string>
<!-- Sign In -->
<string name="browser">Browser</string>
<string name="useBrowser">Use browser:</string>
<string name="signInWith">Sign in with…</string>
<string name="signInWithProvider">Sign in with %s…</string>
<string name="signInWithOpenID">OpenID - Facebook, Twitter, etc.</string>
@ -248,7 +250,6 @@
</plurals>
<string name="sessionsNotImported">Local data is up to date</string>
<string name="signInToImport">Sign in to import session data</string>
<plurals name="daysOfMeditationMinimal">
<item quantity="one">%d day</item>
@ -268,7 +269,6 @@
<string name="signOut">Sign out</string>
<string name="signOutOfMediNET">Sign out of MediNET</string>
<string name="signOutOfMediNETConfirmTitle">Sign out of MediNET?</string>
<string name="signOutOfMediNETConfirm">You are signed in with %s.</string>
<string name="rateMeditationAssistant">Rate Meditation Assistant</string>
<string name="rateMeditationAssistantText">If you have found this application beneficial to your meditation practice, would you please add a rating?\n\nThe paid version of this app contains no ads and features home screen widgets. Please consider purchasing it if you are able to.\n\nThis dialog will not appear again.</string>
<string name="translateMeditationAssistantText">Would you please help Meditation Assistant reach a wider audience by translating it into another language?\n\nTranslating is eaily done through an online interface. You can visit the translation webpage later by clicking Translate from the About window.\n\nThis dialog will not appear again.</string>

View File

@ -1,7 +1,7 @@
package sh.ftp.rocketninelabs.meditationassistant;
/*
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.data.FreezableUtils;
@ -50,6 +50,6 @@ public class WearListenerService extends WearableListenerService {
// Send the RPC
Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
DATA_ITEM_RECEIVED_PATH, payload);
}*/
}
}
}
}*/

View File

@ -1,36 +1,37 @@
buildscript {
repositories {
mavenCentral()
}
/*buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:2.2.1'
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
apply plugin: 'com.android.application'
repositories {
maven {
url "https://maven.google.com"
}
jcenter()
}
android {
compileSdkVersion 24
buildToolsVersion '24.0.1'
compileSdkVersion 26
buildToolsVersion '26.0.1'
defaultConfig {
minSdkVersion 20
targetSdkVersion 24
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false /* Docs say to not compress wear APK */
minifyEnabled false /* Docs say to not compress wear APK *//*
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:support-v4:24.2.0'
compile 'com.google.android.support:wearable:1.1.0'
compile 'com.google.android.gms:play-services-wearable:9.4.0'
}
compile 'com.google.android.gms:play-services-wearable:11.0.4'
}*/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -10,6 +10,7 @@ Download
[paid](https://play.google.com/store/apps/details?id=sh.ftp.rocketninelabs.meditationassistant.full)
- Amazon: [free](http://www.amazon.com/Rocket-Nine-Laboratories-Meditation-Assistant/dp/B00BQVZ9DU) /
[paid](http://www.amazon.com/Rocket-Nine-Laboratories-Meditation-Assistant/dp/B00BQVXDL0/)
- F-Droid: [free](https://f-droid.org/packages/sh.ftp.rocketninelabs.meditationassistant.opensource/)
Remove Ads
@ -19,7 +20,7 @@ Please purchase the paid version of the app to remove advertisements.
Donate
------------
[Click here to donate](https://medinet.ftp.sh/donate/) via PayPal.
[Click here to donate](https://rocketnine.space/donate/) via PayPal.
Get Support

8
TODO
View File

@ -19,3 +19,11 @@ Migrate all icons to vectors
Merge 2x1 and 3x1 widgets into a single size-aware widget
Automatic session upload
Add warning for all MedINET connections <= version 5, as version 6 is used by all still-supported (Android 4.1+) devices
Allow OpenID signin using AppAuth
Update remaining logo icons to new logo, including notification icon, check on new material guides for new format
Set new default theme to use blue that matches logo, offer old theme still as "Holographic Dark"

View File

@ -1,6 +1,6 @@
#Tue Aug 23 13:49:49 PDT 2016
#Sat Sep 16 15:53:03 PDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip

View File

@ -1 +1,3 @@
include ':MeditationAssistant', ':MeditationAssistantWear'
include ':MeditationAssistant'
// TODO: Add when wear app is ready
// , ':MeditationAssistantWear'