Implement intent filter; hide password for now

This commit is contained in:
2020-11-22 17:00:37 -06:00
parent 23f2978a64
commit 0859cec853
10 changed files with 155 additions and 42 deletions

View File

@@ -68,9 +68,16 @@
android:targetActivity=".activities.habits.list.ListHabitsActivity">
<intent-filter android:label="@string/main_activity_title">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="loophabits.org"
android:pathPrefix="/sync" />
</intent-filter>
</activity-alias>
<activity

View File

@@ -34,8 +34,11 @@ abstract class HabitsActivity : BaseActivity() {
appComponent = (applicationContext as HabitsApplication).component
val habit = getHabitFromIntent(appComponent.habitList)
?: appComponent.modelFactory.buildHabit()
var habit = appComponent.modelFactory.buildHabit()
if(intent.action != "android.intent.action.VIEW") {
val intentHabit = getHabitFromIntent(appComponent.habitList)
if (intentHabit != null) habit = intentHabit
}
component = DaggerHabitsActivityComponent
.builder()

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.common.dialogs;
import android.content.*;
import androidx.annotation.*;
import androidx.appcompat.app.*;
import com.google.auto.factory.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.core.ui.callbacks.*;
import org.isoron.uhabits.R;
import butterknife.*;
@AutoFactory(allowSubclasses = true)
public class ConfirmSyncKeyDialog extends AlertDialog
{
@BindString(R.string.sync_confirm)
protected String question;
@BindString(R.string.yes)
protected String yes;
@BindString(R.string.no)
protected String no;
protected ConfirmSyncKeyDialog(@Provided @ActivityContext Context context,
@NonNull OnConfirmedCallback callback)
{
super(context);
ButterKnife.bind(this);
setTitle(R.string.device_sync);
setMessage(question);
setButton(BUTTON_POSITIVE, yes, (dialog, which) -> callback.onConfirmed());
setButton(BUTTON_NEGATIVE, no, (dialog, which) -> {});
}
}

View File

@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.list
import android.app.*
import android.content.*
import android.util.*
import androidx.annotation.*
import dagger.*
import org.isoron.androidbase.activities.*
@@ -31,7 +32,6 @@ import org.isoron.uhabits.activities.habits.edit.*
import org.isoron.uhabits.activities.habits.list.views.*
import org.isoron.uhabits.core.commands.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.preferences.*
import org.isoron.uhabits.core.tasks.*
import org.isoron.uhabits.core.ui.*
import org.isoron.uhabits.core.ui.callbacks.*
@@ -42,13 +42,13 @@ import org.isoron.uhabits.tasks.*
import java.io.*
import javax.inject.*
const val RESULT_IMPORT_DATA = 1
const val RESULT_EXPORT_CSV = 2
const val RESULT_EXPORT_DB = 3
const val RESULT_BUG_REPORT = 4
const val RESULT_REPAIR_DB = 5
const val REQUEST_OPEN_DOCUMENT = 6
const val REQUEST_SETTINGS = 7
const val RESULT_IMPORT_DATA = 101
const val RESULT_EXPORT_CSV = 102
const val RESULT_EXPORT_DB = 103
const val RESULT_BUG_REPORT = 104
const val RESULT_REPAIR_DB = 105
const val REQUEST_OPEN_DOCUMENT = 106
const val REQUEST_SETTINGS = 107
@ActivityScope
class ListHabitsScreen
@@ -63,6 +63,7 @@ class ListHabitsScreen
private val exportDBFactory: ExportDBTaskFactory,
private val importTaskFactory: ImportDataTaskFactory,
private val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
private val confirmSyncKeyDialogFactory: ConfirmSyncKeyDialogFactory,
private val colorPickerFactory: ColorPickerDialogFactory,
private val numberPickerFactory: NumberPickerFactory,
private val behavior: Lazy<ListHabitsBehavior>,
@@ -82,6 +83,12 @@ class ListHabitsScreen
setMenu(menu.get())
setSelectionMenu(selectionMenu.get())
commandRunner.addListener(this)
if(activity.intent.action == "android.intent.action.VIEW") {
val uri = activity.intent.data!!.toString()
val key = uri.replace(Regex("^.*sync/"), "")
Log.i("ListHabitsScreen", key)
behavior.get().onSyncKeyOffer(key)
}
}
fun onDettached() {
@@ -171,13 +178,14 @@ class ListHabitsScreen
override fun showMessage(m: ListHabitsBehavior.Message) {
showMessage(when (m) {
COULD_NOT_EXPORT -> R.string.could_not_export
IMPORT_SUCCESSFUL -> R.string.habits_imported
IMPORT_FAILED -> R.string.could_not_import
DATABASE_REPAIRED -> R.string.database_repaired
COULD_NOT_GENERATE_BUG_REPORT -> R.string.bug_report_failed
FILE_NOT_RECOGNIZED -> R.string.file_not_recognized
})
COULD_NOT_EXPORT -> R.string.could_not_export
IMPORT_SUCCESSFUL -> R.string.habits_imported
IMPORT_FAILED -> R.string.could_not_import
DATABASE_REPAIRED -> R.string.database_repaired
COULD_NOT_GENERATE_BUG_REPORT -> R.string.bug_report_failed
FILE_NOT_RECOGNIZED -> R.string.file_not_recognized
SYNC_ENABLED -> R.string.sync_enabled
})
}
override fun showSendBugReportToDeveloperScreen(log: String) {
@@ -204,6 +212,10 @@ class ListHabitsScreen
numberPickerFactory.create(value, unit, callback).show()
}
override fun showConfirmInstallSyncKey(callback: OnConfirmedCallback) {
activity.showDialog(confirmSyncKeyDialogFactory.create(callback))
}
@StringRes
private fun getExecuteString(command: Command): Int? {
when (command) {

View File

@@ -145,7 +145,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
{
PreferenceCategory devCategory =
(PreferenceCategory) findPreference("devCategory");
devCategory.removeAll();
devCategory.setVisible(false);
}
@@ -159,6 +158,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
private void updateSyncPreferences()
{
if(prefs.getSyncKey().isEmpty()) {
prefs.setSyncEnabled(false);
((CheckBoxPreference) findPreference("pref_sync_enabled")).setChecked(false);
}
findPreference("pref_sync_base_url").setSummary(prefs.getSyncBaseURL());
findPreference("pref_sync_key").setSummary(prefs.getSyncKey());
findPreference("pref_sync_display").setVisible(prefs.isSyncEnabled());

View File

@@ -44,17 +44,16 @@ class SyncActivity : BaseActivity() {
private lateinit var preferences: Preferences
private lateinit var taskRunner: TaskRunner
private lateinit var baseScreen: BaseScreen
private lateinit var themeSwitcher: AndroidThemeSwitcher
private lateinit var binding: ActivitySyncBinding
private var styledResources = StyledResources(this)
override fun onCreate(state: Bundle?) {
super.onCreate(state)
baseScreen = BaseScreen(this)
val component = (application as HabitsApplication).component
themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
themeSwitcher.apply()
taskRunner = component.taskRunner
preferences = component.preferences
@@ -85,7 +84,7 @@ class SyncActivity : BaseActivity() {
}
private fun displayCurrentKey() {
displayLink("${preferences.syncBaseURL}/sync/${preferences.syncKey}")
displayLink("https://loophabits.org/sync/${preferences.syncKey}")
displayPassword("6B2W9F5X")
}
@@ -139,27 +138,36 @@ class SyncActivity : BaseActivity() {
}
private fun displayLink(link: String) {
binding.qrCode.visibility = View.VISIBLE
binding.progress.visibility = View.GONE
binding.qrCode.visibility = View.GONE
binding.progress.visibility = View.VISIBLE
binding.errorPanel.visibility = View.GONE
binding.syncLink.text = link
displayQR(link)
}
private fun displayQR(msg: String) {
val writer = QRCodeWriter()
val matrix = writer.encode(msg, BarcodeFormat.QR_CODE, 1024, 1024)
val height = matrix.height
val width = matrix.width
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
val bgColor = StyledResources(this).getColor(R.attr.highContrastReverseTextColor)
val fgColor = StyledResources(this).getColor(R.attr.highContrastTextColor)
for (x in 0 until width) {
for (y in 0 until height) {
val color = if (matrix.get(x, y)) fgColor else bgColor
bitmap.setPixel(x, y, color)
taskRunner.execute(object : Task {
lateinit var bitmap: Bitmap
override fun doInBackground() {
val writer = QRCodeWriter()
val matrix = writer.encode(msg, BarcodeFormat.QR_CODE, 1024, 1024)
val height = matrix.height
val width = matrix.width
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
val bgColor = styledResources.getColor(R.attr.highContrastReverseTextColor)
val fgColor = styledResources.getColor(R.attr.highContrastTextColor)
for (x in 0 until width) {
for (y in 0 until height) {
val color = if (matrix.get(x, y)) fgColor else bgColor
bitmap.setPixel(x, y, color)
}
}
}
}
binding.qrCode.setImageBitmap(bitmap)
override fun onPostExecute() {
binding.progress.visibility = View.GONE
binding.qrCode.visibility = View.VISIBLE
binding.qrCode.setImageBitmap(bitmap)
}
})
}
}

View File

@@ -123,7 +123,9 @@
</FrameLayout>
<!-- Password -->
<FrameLayout style="@style/FormOuterBox">
<FrameLayout
style="@style/FormOuterBox"
android:visibility="gone">
<LinearLayout style="@style/FormInnerBox">

View File

@@ -206,10 +206,12 @@
<string name="device_sync">Device sync</string>
<string name="pref_sync_summary">When enabled, an encrypted copy of your data will be uploaded to our servers. See privacy policy.</string>
<string name="pref_sync_title">Sync data across devices</string>
<string name="display_sync_code">Display sync instructions</string>
<string name="sync_instructions"><![CDATA[<b>Instructions:</b><br/>1. Install Loop in your second device.<br/>2. Open the link below in your second device.<br/>3. Provide the 8-character password below.<br/><b>Important:</b> Do not publish this information. It gives anyone access to your data.]]></string>
<string name="display_sync_code">Show device sync instructions</string>
<string name="sync_instructions"><![CDATA[<b>Instructions:</b><br/>1. Install Loop in your second device.<br/>2. Open the link below in your second device.<br/><b>Important:</b> Do not not make this information public. It gives anyone access to your data.]]></string>
<string name="sync_link">Sync link</string>
<string name="sync_link_qr">Sync link (QR code)</string>
<string name="password">Password</string>
<string name="copied_to_the_clipboard">Copied to the clipboard</string>
<string name="sync_confirm"><![CDATA[Are you trying to enable device sync?\n\nThis feature allows you to sync your data across multiple devices. When enabled, an encrypted copy of your data will be uploaded to our servers.]]></string>
<string name="sync_enabled">Device sync enabled</string>
</resources>

View File

@@ -329,6 +329,11 @@ public class Preferences
return storage.getBoolean("pref_sync_enabled", false);
}
public void setSyncEnabled(boolean enabled)
{
storage.putBoolean("pref_sync_enabled", enabled);
}
/**
* @return An integer representing the first day of the week. Sunday

View File

@@ -25,7 +25,9 @@ import org.isoron.uhabits.core.commands.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.core.ui.callbacks.*;
import org.isoron.uhabits.core.utils.*;
import org.jetbrains.annotations.*;
import java.io.*;
import java.util.*;
@@ -156,10 +158,19 @@ public class ListHabitsBehavior
habit.getId());
}
public void onSyncKeyOffer(@NotNull String key)
{
screen.showConfirmInstallSyncKey(() -> {
prefs.setSyncKey(key);
prefs.setSyncEnabled(true);
screen.showMessage(Message.SYNC_ENABLED);
});
}
public enum Message
{
COULD_NOT_EXPORT, IMPORT_SUCCESSFUL, IMPORT_FAILED, DATABASE_REPAIRED,
COULD_NOT_GENERATE_BUG_REPORT, FILE_NOT_RECOGNIZED
COULD_NOT_GENERATE_BUG_REPORT, FILE_NOT_RECOGNIZED, SYNC_ENABLED
}
public interface BugReporter
@@ -196,5 +207,7 @@ public class ListHabitsBehavior
void showSendBugReportToDeveloperScreen(String log);
void showSendFileScreen(@NonNull String filename);
void showConfirmInstallSyncKey(@NonNull OnConfirmedCallback callback);
}
}