diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..03b5efc --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,51 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.2" + defaultConfig { + applicationId "ca.mudar.rotationquicksetting" + minSdkVersion 24 + targetSdkVersion 25 + versionCode 10 + versionName "0.5-alpha" + + resConfigs "en", "fr" + } + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + + ext.enableCrashlytics = true + } + + debug { + applicationIdSuffix ".d" + versionNameSuffix "-build-" + getDate() + minifyEnabled false + + ext.enableCrashlytics = false + } + + debugCrashlytics.initWith(buildTypes.debug) + debugCrashlytics { + applicationIdSuffix "" + + buildConfigField "boolean", "USE_CRASHLYTICS", "true" + ext.enableCrashlytics = true + } + } +} + +def static getDate() { + new Date().format('ddMMMyyyy.HHmm') +} + +ext.supportLibVersion = '25.3.1' + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "com.android.support:appcompat-v7:$supportLibVersion" + compile "com.android.support:design:$supportLibVersion" +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..147347b --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/mudar/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2273e8d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/ca/mudar/rotationquicksetting/Const.java b/app/src/main/java/ca/mudar/rotationquicksetting/Const.java new file mode 100644 index 0000000..d37ae39 --- /dev/null +++ b/app/src/main/java/ca/mudar/rotationquicksetting/Const.java @@ -0,0 +1,21 @@ +package ca.mudar.rotationquicksetting; + +/** + * Created by mudar on 10/05/17. + */ + +public class Const { + + /** + * SharedPreferences + */ + public static final String APP_PREFS_NAME = "orientation_tile_prefs"; + + public interface PrefsNames { + String REVERSE_PORT = "prefs_reverse_port"; + String REVERSE_LAND = "prefs_reverse_land"; + String AUTO_ROTATE = "prefs_auto_rotate"; + String PERMISSION_GRANTED = "prefs_permission_granted"; + String ABOUT = "prefs_about"; + } +} diff --git a/app/src/main/java/ca/mudar/rotationquicksetting/data/UserPrefs.java b/app/src/main/java/ca/mudar/rotationquicksetting/data/UserPrefs.java new file mode 100644 index 0000000..bf9c236 --- /dev/null +++ b/app/src/main/java/ca/mudar/rotationquicksetting/data/UserPrefs.java @@ -0,0 +1,59 @@ +package ca.mudar.rotationquicksetting.data; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import ca.mudar.rotationquicksetting.Const; +import ca.mudar.rotationquicksetting.Const.PrefsNames; +import ca.mudar.rotationquicksetting.R; + +/** + * Created by mudar on 10/05/17. + */ + +public class UserPrefs { + + private static UserPrefs instance; + private SharedPreferences mPrefs; + private SharedPreferences.Editor mPrefsEditor; + + private UserPrefs(Context context) { + mPrefs = context.getSharedPreferences(Const.APP_PREFS_NAME, Context.MODE_PRIVATE); + } + + public static UserPrefs getInstance(Context context) { + if (instance == null) { + instance = new UserPrefs(context); + } + return instance; + } + + private SharedPreferences.Editor edit() { + if (mPrefsEditor == null) { + mPrefsEditor = mPrefs.edit(); + } + + return mPrefsEditor; + } + + public static void setDefaults(Context context) { + PreferenceManager.setDefaultValues(context, + Const.APP_PREFS_NAME, + Context.MODE_PRIVATE, + R.xml.prefs_defaults, + false); + } + + public boolean isReversePortrait() { + return mPrefs.getBoolean(PrefsNames.REVERSE_PORT, false); + } + + public boolean isReverseLandscape() { + return mPrefs.getBoolean(PrefsNames.REVERSE_LAND, false); + } + + public boolean hasAutoRotate() { + return mPrefs.getBoolean(PrefsNames.AUTO_ROTATE, false); + } +} diff --git a/app/src/main/java/ca/mudar/rotationquicksetting/service/QuickSettingsService.java b/app/src/main/java/ca/mudar/rotationquicksetting/service/QuickSettingsService.java new file mode 100644 index 0000000..a43cf13 --- /dev/null +++ b/app/src/main/java/ca/mudar/rotationquicksetting/service/QuickSettingsService.java @@ -0,0 +1,182 @@ +package ca.mudar.rotationquicksetting.service; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.service.quicksettings.TileService; +import android.support.v4.app.NotificationCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.WindowManager; + +import ca.mudar.rotationquicksetting.R; +import ca.mudar.rotationquicksetting.data.UserPrefs; +import ca.mudar.rotationquicksetting.utils.OrientationUtils; + +/** + * Created by mudar on 07/05/17. + */ + +public class QuickSettingsService extends TileService { + private final static String TAG = "QuickSettingsService"; + + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "onCreate: "); + } + + @Override + public void onStartListening() { + super.onStartListening(); + Log.i(TAG, "onStartListening: "); + + try { + updateQuickSettingsTile(getCurrentOrientation(UserPrefs.getInstance(getApplicationContext()).hasAutoRotate()), + Settings.System.canWrite(getApplicationContext())); + } catch (Settings.SettingNotFoundException e) { + e.printStackTrace(); + } + } + + @Override + public void onStopListening() { + super.onStopListening(); + Log.i(TAG, "onStopListening: "); + } + + @Override + public void onClick() { + Log.i(TAG, "onClick: "); + + if (Settings.System.canWrite(getApplicationContext())) { + try { + final int newOrientation = toggleOrientation(); + updateQuickSettingsTile(newOrientation, true); + } catch (Settings.SettingNotFoundException e) { + e.printStackTrace(); + } + } else { + showPermissionNotification(); + } + } + + private void updateQuickSettingsTile(int orientation, boolean canWriteSettings) throws Settings.SettingNotFoundException { + Log.d(TAG, "updateQuickSettingsTile() called with: orientation = [" + orientation + "]"); + final Tile tile = getQsTile(); + tile.setState(canWriteSettings ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); + + if (OrientationUtils.isLandscape(orientation)) { + tile.setIcon(Icon.createWithResource(getApplicationContext(), R.drawable.ic_screen_landscape)); + tile.setLabel(getString(R.string.tile_label_land)); + } else if (OrientationUtils.isPortrait(orientation)) { + tile.setIcon(Icon.createWithResource(getApplicationContext(), R.drawable.ic_screen_portrait)); + tile.setLabel(getString(R.string.tile_label_port)); + } else { + tile.setIcon(Icon.createWithResource(getApplicationContext(), R.drawable.ic_screen_rotation)); + tile.setLabel(getString(R.string.tile_label_auto_rotate)); + } + + tile.updateTile(); + } + + /** + * Toggles current location, and returns the new value. + * Requires a prior check of Settings.System.canWrite() + * + * @return new rotation + */ + private int toggleOrientation() throws Settings.SettingNotFoundException { + Log.i(TAG, "toggleOrientation: "); + final ContentResolver contentResolver = getContentResolver(); + final UserPrefs userPrefs = UserPrefs.getInstance(getApplicationContext()); + + if (userPrefs.hasAutoRotate() && + Settings.System.getInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION) == 0) { + // Enable auto-rotation + Log.e(TAG, "Enable auto-rotation"); + Settings.System.putInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION, 1); + return OrientationUtils.ROTATION_AUTO; + } + + final int oldOrientation = getCurrentOrientation(false); + final int newOrientation = OrientationUtils.getOppositeOrientation(oldOrientation, userPrefs); + + Settings.System.putInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION, 0); + Settings.System.putInt(contentResolver, Settings.System.USER_ROTATION, newOrientation); + + return newOrientation; + } + + private int getCurrentOrientation(boolean includeAutoRotate) throws Settings.SettingNotFoundException { + Log.i(TAG, "getCurrentOrientation: "); + final ContentResolver contentResolver = getContentResolver(); + final boolean hasAccelerometer = (Settings.System + .getInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION) == 1); + + if (hasAccelerometer && includeAutoRotate) { + return OrientationUtils.ROTATION_AUTO; + } + + return hasAccelerometer ? getAccelerometerOrientation() : getUserOrientation(); + } + + private int getUserOrientation() throws Settings.SettingNotFoundException { + Log.i(TAG, "getUserOrientation: "); + return Settings.System.getInt(getContentResolver(), Settings.System.USER_ROTATION); + } + + private int getAccelerometerOrientation() throws Settings.SettingNotFoundException { + Log.i(TAG, "getAccelerometerOrientation: "); + final WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); + return windowManager.getDefaultDisplay().getRotation(); + } + + /** + * Create and show a simple notification containing the received GCM message. + */ + private void showPermissionNotification() { + final Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, + Uri.parse("package:" + getPackageName())); + + final PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), + 0, + intent, + PendingIntent.FLAG_ONE_SHOT); + + final Resources res = getApplicationContext().getResources(); + final String contentTitle = res.getString(R.string.notify_permissions_title); + final String contentText = res.getString(R.string.notify_permissions_text); + + final Drawable drawable = ContextCompat.getDrawable(getApplicationContext(), R.mipmap.ic_launcher); + final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); + + final NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) + .setSmallIcon(R.drawable.ic_screen_lock_rotation) + .setLargeIcon(bitmap) + .setColor(getColor(R.color.app_notification)) + .setContentTitle(contentTitle) + .setContentText(contentText) + .setOngoing(true) + .setShowWhen(false) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setCategory(NotificationCompat.CATEGORY_MESSAGE) + .setContentIntent(pendingIntent); + + ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) + .notify(0, builder.build()); + } + +} diff --git a/app/src/main/java/ca/mudar/rotationquicksetting/ui/AboutBottomSheetFragment.java b/app/src/main/java/ca/mudar/rotationquicksetting/ui/AboutBottomSheetFragment.java new file mode 100644 index 0000000..694c351 --- /dev/null +++ b/app/src/main/java/ca/mudar/rotationquicksetting/ui/AboutBottomSheetFragment.java @@ -0,0 +1,58 @@ +package ca.mudar.rotationquicksetting.ui; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.support.design.widget.BottomSheetDialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import ca.mudar.rotationquicksetting.R; + +/** + * Created by mudar on 16/05/17. + */ + +public class AboutBottomSheetFragment extends BottomSheetDialogFragment implements View.OnClickListener { + public static AboutBottomSheetFragment newInstance() { + return new AboutBottomSheetFragment(); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.fragment_about, container, false); + + setupListeners(view); + + return view; + } + + @Override + public void onClick(View v) { + final int id = v.getId(); + + if (id == R.id.about_credits) { + showWebsite(R.string.url_mudar_ca); + } else if (id == R.id.about_source_code) { + showWebsite(R.string.url_github); + } else if (id == R.id.about_rate_app) { + showWebsite(R.string.url_playstore); + } + } + + private void setupListeners(View view) { + view.findViewById(R.id.about_credits).setOnClickListener(this); + view.findViewById(R.id.about_source_code).setOnClickListener(this); + view.findViewById(R.id.about_rate_app).setOnClickListener(this); + } + + private void showWebsite(@StringRes int website) { + final Intent viewIntent = new Intent(Intent.ACTION_VIEW); + viewIntent.setData(Uri.parse(getResources().getString(website))); + startActivity(viewIntent); + } +} diff --git a/app/src/main/java/ca/mudar/rotationquicksetting/ui/MainActivity.java b/app/src/main/java/ca/mudar/rotationquicksetting/ui/MainActivity.java new file mode 100644 index 0000000..82b6893 --- /dev/null +++ b/app/src/main/java/ca/mudar/rotationquicksetting/ui/MainActivity.java @@ -0,0 +1,42 @@ +package ca.mudar.rotationquicksetting.ui; + +import android.app.Fragment; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import ca.mudar.rotationquicksetting.data.UserPrefs; + +/** + * Created by mudar on 10/05/17. + */ + +public class MainActivity extends AppCompatActivity implements + SettingsFragment.SettingsAboutCallback { + private final static String FRAGMENT_TAG_SETTINGS = "f_settings"; + private final static String FRAGMENT_TAG_ABOUT = "f_about"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set default preferences + UserPrefs.setDefaults(getApplicationContext()); + + if (savedInstanceState == null) { + final Fragment fragment = SettingsFragment.newInstance(); + getFragmentManager().beginTransaction() + .replace(android.R.id.content, fragment, FRAGMENT_TAG_SETTINGS) + .commit(); + } + } + + /** + * Implements SettingsFragment.SettingsAboutCallback + * Shows about the About BottomSheetDialogFragment + */ + @Override + public void onShowAbout() { + final AboutBottomSheetFragment bottomSheet = AboutBottomSheetFragment.newInstance(); + bottomSheet.show(getSupportFragmentManager(), FRAGMENT_TAG_ABOUT); + } +} diff --git a/app/src/main/java/ca/mudar/rotationquicksetting/ui/SettingsFragment.java b/app/src/main/java/ca/mudar/rotationquicksetting/ui/SettingsFragment.java new file mode 100644 index 0000000..caef5f4 --- /dev/null +++ b/app/src/main/java/ca/mudar/rotationquicksetting/ui/SettingsFragment.java @@ -0,0 +1,194 @@ +package ca.mudar.rotationquicksetting.ui; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.provider.Settings; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.support.design.widget.Snackbar; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.View; + +import ca.mudar.rotationquicksetting.BuildConfig; +import ca.mudar.rotationquicksetting.Const; +import ca.mudar.rotationquicksetting.Const.PrefsNames; +import ca.mudar.rotationquicksetting.R; + +import static android.content.ContentValues.TAG; +import static ca.mudar.rotationquicksetting.utils.OrientationUtils.ROTATION_LAND; +import static ca.mudar.rotationquicksetting.utils.OrientationUtils.ROTATION_LAND_REVERSE; +import static ca.mudar.rotationquicksetting.utils.OrientationUtils.ROTATION_PORT; +import static ca.mudar.rotationquicksetting.utils.OrientationUtils.ROTATION_PORT_REVERSE; + +/** + * Created by mudar on 10/05/17. + */ + +public class SettingsFragment extends PreferenceFragment implements + SharedPreferences.OnSharedPreferenceChangeListener, + Preference.OnPreferenceClickListener { + + public static SettingsFragment newInstance() { + return new SettingsFragment(); + } + + private SharedPreferences mSharedPrefs; + private Preference mPermissionGranted; + private SettingsAboutCallback mListener; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + if (context instanceof SettingsAboutCallback) { + mListener = (SettingsAboutCallback) context; + } + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + + final PreferenceManager pm = this.getPreferenceManager(); + pm.setSharedPreferencesName(Const.APP_PREFS_NAME); + pm.setSharedPreferencesMode(Context.MODE_PRIVATE); + + addPreferencesFromResource(R.xml.prefs_settings); + mPermissionGranted = findPreference(PrefsNames.PERMISSION_GRANTED); + + mSharedPrefs = pm.getSharedPreferences(); + + mSharedPrefs.registerOnSharedPreferenceChangeListener(this); + + mPermissionGranted.setOnPreferenceClickListener(this); + findPreference(PrefsNames.ABOUT).setOnPreferenceClickListener(this); + } + + @Override + public void onResume() { + super.onResume(); + + setupSummaries(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + /** + * Remove the listener + */ + if (mSharedPrefs != null) { + mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); + } + } + + /** + * Implements SharedPreferences.OnSharedPreferenceChangeListener + * + * @param sharedPreferences + * @param key + */ + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (PrefsNames.REVERSE_PORT.equals(key)) { + showTryRotationSnackbar(sharedPreferences.getBoolean(key, false) ? + ROTATION_PORT_REVERSE : ROTATION_PORT); + } else if (PrefsNames.REVERSE_LAND.equals(key)) { + showTryRotationSnackbar(sharedPreferences.getBoolean(key, false) ? + ROTATION_LAND_REVERSE : ROTATION_LAND); + } + } + + /** + * Implements Preference.OnPreferenceClickListener + * + * @param preference + * @return + */ + @Override + public boolean onPreferenceClick(Preference preference) { + final String key = preference.getKey(); + if (PrefsNames.PERMISSION_GRANTED.equals(key)) { + final Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, + Uri.parse("package:" + getContext().getPackageName())); + startActivity(intent); + return true; + } else if (PrefsNames.ABOUT.equals(key) && mListener != null) { + Log.i(TAG, "onPreferenceClick: ABOUT"); + mListener.onShowAbout(); + return true; + } + + return false; + } + + private void setupSummaries() { + mPermissionGranted.setSummary(getPermissionGrantedSummary()); + + findPreference(PrefsNames.ABOUT).setSummary(getString( + R.string.prefs_version_title, BuildConfig.VERSION_NAME)); + } + + @StringRes + private int getPermissionGrantedSummary() { + return Settings.System.canWrite(getContext()) ? + R.string.prefs_permission_granted_on : R.string.prefs_permission_granted_off; + } + + private void setOrientation(int orientation) { + if (!Settings.System.canWrite(getContext())) { + return; + } + + final ContentResolver contentResolver = getContext().getContentResolver(); + Settings.System.putInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION, 0); + Settings.System.putInt(contentResolver, Settings.System.USER_ROTATION, orientation); + } + + private void showTryRotationSnackbar(final int orientation) { + if (getView() == null || !Settings.System.canWrite(getContext())) { + return; + } + + @StringRes int message; + switch (orientation) { + case ROTATION_PORT: + message = R.string.snackbar_try_rotation_port; + break; + case ROTATION_PORT_REVERSE: + message = R.string.snackbar_try_rotation_port_reverse; + break; + case ROTATION_LAND: + message = R.string.snackbar_try_rotation_land; + break; + case ROTATION_LAND_REVERSE: + message = R.string.snackbar_try_rotation_land_reverse; + break; + default: + return; + } + Snackbar.make(getView(), message, Snackbar.LENGTH_LONG) + .setAction(R.string.btn_try_it, new View.OnClickListener() { + @Override + public void onClick(View v) { + setOrientation(orientation); + } + }) + .setActionTextColor(ContextCompat.getColor(getContext(), R.color.snackbar_action_text)) + .show(); + } + + public interface SettingsAboutCallback { + public void onShowAbout(); + } +} diff --git a/app/src/main/java/ca/mudar/rotationquicksetting/utils/OrientationUtils.java b/app/src/main/java/ca/mudar/rotationquicksetting/utils/OrientationUtils.java new file mode 100644 index 0000000..310ee4b --- /dev/null +++ b/app/src/main/java/ca/mudar/rotationquicksetting/utils/OrientationUtils.java @@ -0,0 +1,36 @@ +package ca.mudar.rotationquicksetting.utils; + +import android.support.annotation.NonNull; +import android.view.Surface; + +import ca.mudar.rotationquicksetting.data.UserPrefs; + +/** + * Created by mudar on 10/05/17. + */ + +public class OrientationUtils { + public final static int ROTATION_PORT = Surface.ROTATION_0; + public final static int ROTATION_PORT_REVERSE = Surface.ROTATION_180; + public final static int ROTATION_LAND = Surface.ROTATION_90; + public final static int ROTATION_LAND_REVERSE = Surface.ROTATION_270; + public final static int ROTATION_AUTO = -1; + + public static int getOppositeOrientation(int orientation, @NonNull UserPrefs prefs) { + if (isPortrait(orientation)) { + return prefs.isReverseLandscape() ? ROTATION_LAND_REVERSE : ROTATION_LAND; + } else if (isLandscape(orientation)) { + return prefs.isReversePortrait() ? ROTATION_PORT_REVERSE : ROTATION_PORT; + } else { + return ROTATION_AUTO; + } + } + + public static boolean isPortrait(int orientation) { + return (orientation == ROTATION_PORT) || (orientation == ROTATION_PORT_REVERSE); + } + + public static boolean isLandscape(int orientation) { + return (orientation == ROTATION_LAND) || (orientation == ROTATION_LAND_REVERSE); + } +} diff --git a/app/src/main/res/drawable/ic_github_mark.xml b/app/src/main/res/drawable/ic_github_mark.xml new file mode 100644 index 0000000..4b59c9f --- /dev/null +++ b/app/src/main/res/drawable/ic_github_mark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_person.xml b/app/src/main/res/drawable/ic_person.xml new file mode 100644 index 0000000..0bbcaed --- /dev/null +++ b/app/src/main/res/drawable/ic_person.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_screen_landscape.xml b/app/src/main/res/drawable/ic_screen_landscape.xml new file mode 100644 index 0000000..9314079 --- /dev/null +++ b/app/src/main/res/drawable/ic_screen_landscape.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_screen_lock_rotation.xml b/app/src/main/res/drawable/ic_screen_lock_rotation.xml new file mode 100644 index 0000000..f427d32 --- /dev/null +++ b/app/src/main/res/drawable/ic_screen_lock_rotation.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_screen_portrait.xml b/app/src/main/res/drawable/ic_screen_portrait.xml new file mode 100644 index 0000000..50c70b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_screen_portrait.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_screen_rotation.xml b/app/src/main/res/drawable/ic_screen_rotation.xml new file mode 100644 index 0000000..3429c6b --- /dev/null +++ b/app/src/main/res/drawable/ic_screen_rotation.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_security.xml b/app/src/main/res/drawable/ic_security.xml new file mode 100644 index 0000000..78b47e4 --- /dev/null +++ b/app/src/main/res/drawable/ic_security.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_swap_horiz_circle.xml b/app/src/main/res/drawable/ic_swap_horiz_circle.xml new file mode 100644 index 0000000..b884170 --- /dev/null +++ b/app/src/main/res/drawable/ic_swap_horiz_circle.xml @@ -0,0 +1,6 @@ + + + diff --git a/app/src/main/res/drawable/ic_swap_vert_circle.xml b/app/src/main/res/drawable/ic_swap_vert_circle.xml new file mode 100644 index 0000000..2475ded --- /dev/null +++ b/app/src/main/res/drawable/ic_swap_vert_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_thumb_up.xml b/app/src/main/res/drawable/ic_thumb_up.xml new file mode 100644 index 0000000..35feaab --- /dev/null +++ b/app/src/main/res/drawable/ic_thumb_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml new file mode 100644 index 0000000..11eaeeb --- /dev/null +++ b/app/src/main/res/layout/fragment_about.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..2dd159a Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..9d4f866 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..95e7111 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..e72d0ee Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..6b249d1 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..2032dce --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,5 @@ + + + 64dp + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..8cee067 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,34 @@ + + + + + + + #CE93D8 + #9C27B0 + #8E24AA + #7B1FA2 + #6A1B9A + #4A148C + #2196F3 + #FFEB3B + #FFC107 + #AA00FF + #212121 + + + + + + @color/purple_500 + @color/purple_700 + @color/purple_a700 + @color/grey_900 + + + @color/color_primary_dark + @color/purple_900 + @color/icon_prefs + @color/purple_200 + @color/color_primary_dark + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..fb4f6ea --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,10 @@ + + + 16dp + 16dp + + 16dp + 48dp + 72dp + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..5c1836e --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,45 @@ + + Rotation Quick Setting + Rotation QS + Portrait + Landscape + Auto-rotate + System permission request + Tap to allow modifying screen rotation setting. + + + Permission + Rotation + About + Modify settings permission + Granted + Not granted, app is disabled + Reverse portrait + Reverse landscape + Include auto-rotate + About app + Version %s + + + Quick settings tile to select portrait or landscape screen orientation. + + Android app developed by Mudar Noufal. + Open source project, available on GitHub. Source code is released under the GPLv3 license from the Free Software Foundation. + Please share or rate the app if you like it! + + + Try it + + + + Portrait rotation is set to 0 degrees. + Portrait rotation is set to 180 degrees (upside down). + Landscape rotation is set to 90 degrees clockwise. + Landscape rotation is set to 90 degrees counterclockwise. + + + Credits + Source code + Rate app + + diff --git a/app/src/main/res/values/strings_common.xml b/app/src/main/res/values/strings_common.xml new file mode 100644 index 0000000..781789e --- /dev/null +++ b/app/src/main/res/values/strings_common.xml @@ -0,0 +1,15 @@ + + + + http://play.google.com/store/apps/details?id=ca.mudar.orientationtile + http://www.mudar.ca/ + https://github.com/mudar/ScreenOrientationTile + + prefs_permission_granted + prefs_reverse_port + prefs_reverse_land + prefs_auto_rotate + prefs_about + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..9a377a6 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/xml/prefs_defaults.xml b/app/src/main/res/xml/prefs_defaults.xml new file mode 100644 index 0000000..aacb55a --- /dev/null +++ b/app/src/main/res/xml/prefs_defaults.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/prefs_settings.xml b/app/src/main/res/xml/prefs_settings.xml new file mode 100644 index 0000000..5f47b29 --- /dev/null +++ b/app/src/main/res/xml/prefs_settings.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..d0aa704 --- /dev/null +++ b/build.gradle @@ -0,0 +1,23 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..024b1bb --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun May 07 17:50:37 EDT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/playstore/EN/01_main.png b/playstore/EN/01_main.png new file mode 100644 index 0000000..deb2045 Binary files /dev/null and b/playstore/EN/01_main.png differ diff --git a/playstore/EN/02_quick_settings.png b/playstore/EN/02_quick_settings.png new file mode 100644 index 0000000..6c96e6a Binary files /dev/null and b/playstore/EN/02_quick_settings.png differ diff --git a/playstore/EN/description-en.md b/playstore/EN/description-en.md new file mode 100644 index 0000000..d0d5762 --- /dev/null +++ b/playstore/EN/description-en.md @@ -0,0 +1,14 @@ +## Title +*30 chars* + +Rotation Quick Setting + +## Short description +*80 chars* + +Quick settings tile to select portrait or landscape screen orientation. + +## Full description +*4000 chars* + +Intended for Nexus 7 devices with accelerometer auto-rotation issues. diff --git a/playstore/web_hi_res_512.png b/playstore/web_hi_res_512.png new file mode 100644 index 0000000..17c2338 Binary files /dev/null and b/playstore/web_hi_res_512.png differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app'